diff -r fb51f5b4aa4c CHANGELOG
--- a/CHANGELOG	Mon Sep 24 11:36:39 2012 +0200
+++ b/CHANGELOG	Mon Sep 24 20:36:36 2012 +0200
@@ -1,3 +1,10 @@
+* Add RPC definition
+* Repace BrowseRecord by Model instance
+* Replace Cache decorator by a simple LRU Cache
+* Remove Cacheable
+* Remove _description
+* Rename _name by __name__
+* Use class in Pool
 * Fix search button clause in ModelButton.get_groups (CVE-2012-2238)
 * Merge all kind of buttons
 * Use XML id for board action instead of id
diff -r fb51f5b4aa4c TODO
--- a/TODO	Mon Sep 24 11:36:39 2012 +0200
+++ b/TODO	Mon Sep 24 20:36:36 2012 +0200
@@ -1,7 +1,7 @@
+- Allow to pickle Model instance
 - Add check on action groups when executing wizard
 - Allow to create fields from the client interface
 - Improve encoding of default properties
 - Add check for index and constraint name lenght (limit 31)
-- Change _constraint to pass a BrowseRecordList instead of a list of ids
 - Add order attribute on page tag
 - Find the on_change method for inherited fields
diff -r fb51f5b4aa4c doc/ref/index.rst
--- a/doc/ref/index.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/ref/index.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -13,3 +13,4 @@
     transaction
     tools/singleton
     pool
+    rpc
diff -r fb51f5b4aa4c doc/ref/models/fields.rst
--- a/doc/ref/models/fields.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/ref/models/fields.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -146,6 +146,13 @@
 
 Define how the field must be loaded: ``lazy`` or ``eager``.
 
+``name``
+--------
+
+.. attribute:: Field.name
+
+The name of the field.
+
 Default value
 =============
 
diff -r fb51f5b4aa4c doc/ref/models/models.rst
--- a/doc/ref/models/models.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/ref/models/models.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -5,14 +5,14 @@
 Model
 =====
 
-.. class:: Model
+.. class:: Model([id[, \**kwargs]])
 
 This is the base class that every kind of :ref:`model <topics-models>`
 inherits. It defines common attributes of all models.
 
 Class attributes are:
 
-.. attribute:: Model._name
+.. attribute:: Model.__name__
 
     It contains the a unique name to reference the model throughout the
     platform.
@@ -28,15 +28,10 @@
 
     .. _generalization: http://en.wikipedia.org/wiki/Class_diagram#Generalization
 
-.. attribute:: Model._description
+.. attribute:: Model.__rpc__
 
-    It contains a description of the model.
-
-.. attribute:: Model._rpc
-
-    It contains a dictionary with method name as key and a boolean as value. If
-    a method name is in the dictionary then it is allowed to call it remotely.
-    If the value is ``True`` then the transaction will be committed.
+    It contains a dictionary with method name as key and an instance of
+    :class:`trytond.rpc.RPC` as value.
 
 .. attribute:: Model._error_messages
 
@@ -57,13 +52,21 @@
 
     The definition of the field ``id`` of records.
 
-Instance methods:
+Class methods:
 
-.. method:: Model.init(module_name)
+.. classmethod:: Model.__setup__()
+
+    Setup the class before adding into the :class:`trytond.pool.Pool`.
+
+.. classmethod:: Model.__post_setup__()
+
+    Setup the class after added into the :class:`trytond.pool.Pool`.
+
+.. classmethod:: Model.__register__(module_name)
 
     Registers the model in ``ir.model`` and ``ir.model.field``.
 
-.. method:: Model.raise_user_error(error[, error_args[, error_description[, error_description_args[, raise_exception]]]])
+.. classmethod:: Model.raise_user_error(error[, error_args[, error_description[, error_description_args[, raise_exception]]]])
 
     Raises an exception that will be displayed as an error message in the
     client.  ``error`` is the key of the error message in ``_error_messages``
@@ -72,7 +75,7 @@
     The boolean ``raise_exception`` can be set to ``False`` to retrieve the
     error message strings.
 
-.. method:: Model.raise_user_warning(warning_name, warning[, warning_args[, warning_description[, warning_description_args]]])
+.. classmethod:: Model.raise_user_warning(warning_name, warning[, warning_args[, warning_description[, warning_description_args]]])
 
     Raises an exception that will be displayed as a warning message on the
     client, if the user has not yet bypassed it. ``warning_name`` is used to
@@ -84,14 +87,14 @@
         warning states by users.
     ..
 
-.. method:: Model.default_get(fields_names[, with_rec_name])
+.. classmethod:: Model.default_get(fields_names[, with_rec_name])
 
     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.
 
-.. method:: Model.fields_get([fields_names])
+.. classmethod:: Model.fields_get([fields_names])
 
     Return the definition of each field on the model.
 
@@ -122,9 +125,9 @@
     Same as :stat:`ModelView.button` but return the action id of the XML `id`
     action.
 
-Instance methods:
+Class methods:
 
-.. method:: ModelView.fields_view_get([view_id[, view_type[, toolbar]]])
+.. classmethod:: ModelView.fields_view_get([view_id[, view_type[, toolbar]]])
 
     Return a view definition used by the client. The definition is::
 
@@ -149,9 +152,16 @@
             },
         }
 
-.. method:: ModelView.view_header_get(value[, view_type])
+.. classmethod:: ModelView.view_toolbar_get()
 
-    Return the window title used by the client for the specific view type.
+    Returns the model specific actions in a dictionary with keys:
+        - `print`: a list of available reports
+        - `action`: a list of available actions
+        - `relate`: a list of available relations
+
+.. classmethod:: ModelView.view_header_get(value[, view_type])
+
+    Returns the window title used by the client for the specific view type.
 
 ============
 ModelStorage
@@ -199,121 +209,124 @@
 
         [ ('function name', 'error keyword'), ... ]
 
-    where ``function name`` is the name of an instance method of the class
+    where ``function name`` is the name of an instance or a class method of the
     which must return a boolean (``False`` when the constraint is violated) and
-    ``error keyword`` is a key of
-    :attr:`Model._error_messages`.
+    ``error keyword`` is a key of :attr:`Model._error_messages`.
 
-Instance methods:
+Static methods:
 
-.. method:: ModelStorage.default_create_uid()
+.. staticmethod:: ModelStorage.default_create_uid()
 
     Return the default value for :attr:`create_uid`.
 
-.. method:: ModelStorage.default_create_date()
+.. staticmethod:: ModelStorage.default_create_date()
 
     Return the default value for :attr:`create_date`.
 
-.. method:: ModelStorage.create(values)
+CLass methods:
+
+.. classmethod:: ModelStorage.create(values)
 
     Create a record. ``values`` is a dictionary with fields names as key and
-    created values as value.
+    created values as value and return the new instance.
 
-.. method:: ModelStorage.trigger_create(id)
+.. classmethod:: ModelStorage.trigger_create(record)
 
     Trigger create actions. It will call actions defined in ``ir.trigger`` if
     ``on_create`` is set and ``condition`` is true.
 
-.. method:: ModelStorage.read(ids[, fields_names])
+.. classmethod:: ModelStorage.read(ids[, fields_names])
 
-    Return values for the ids. If ``fields_names`` is set, there will be only
-    values for these fields otherwise it will be for all fields.
-    If ``ids`` is a list of ids, the returned value will be a list of
-    dictionaries.
-    If ``ids`` is an integer, the returned value will be a dictionary.
+    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.
 
-.. method:: ModelStorage.write(ids, values)
+.. classmethod:: ModelStorage.write(records, values)
 
-    Write ``values`` on records. ``ids`` can be a list of ids or an id.
-    ``values`` is a dictionary with fields names as key and writen values as
-    value.
+    Write ``values`` on the list of records.  ``values`` is a dictionary with
+    fields names as key and writen values as value.
 
-.. method:: ModelStorage.trigger_write_get_eligibles(ids)
+.. classmethod:: ModelStorage.trigger_write_get_eligibles(records)
 
-    Return eligible record ids for write actions by triggers. This dictionary
+    Return eligible records for write actions by triggers. This dictionary
     is to pass to :meth:`~ModelStorage.trigger_write`.
 
-.. method:: ModelStorage.trigger_write(eligibles)
+.. classmethod:: ModelStorage.trigger_write(eligibles)
 
     Trigger write actions. It will call actions defined in ``ir.trigger`` if
     ``on_write`` is set and ``condition`` was false before
     :meth:`~ModelStorage.write` and true after.
 
-.. method:: ModelStorage.delete(ids)
+.. classmethod:: ModelStorage.delete(records)
 
-    Delete records. ``ids`` can be a list of ids or an id.
+    Delete records.
 
-.. method:: ModelStorage.trigger_delete(ids)
+.. classmethod:: ModelStorage.trigger_delete(records)
 
     Trigger delete actions. It will call actions defined in ``ir.trigger`` if
     ``on_delete`` is set and ``condition`` is true.
 
-.. method:: ModelStorage.copy(ids[, default])
+.. classmethod:: ModelStorage.copy(records[, default])
 
-    Duplicate the records. ``ids`` can be a list of ids or an id. ``default``
-    is a dictionary of default value for the created records.
+    Duplicate the records. ``default`` is a dictionary of default value for the
+    created records.
 
-.. method:: ModelStorage.search(domain[, offset[, limit[, order[, count]]]])
+.. classmethod:: ModelStorage.search(domain[, offset[, limit[, order[, count]]]])
 
-    Return a list of ids that match the :ref:`domain <topics-domain>`.
+    Return a list of records that match the :ref:`domain <topics-domain>`.
 
-.. method:: ModelStorage.search_count(domain)
+.. classmethod:: ModelStorage.search_count(domain)
 
     Return the number of records that match the :ref:`domain <topics-domain>`.
 
-.. method:: ModelStorage.search_read(domain[, offset[, limit[, order[, fields_names]]]])
+.. classmethod:: ModelStorage.search_read(domain[, offset[, limit[, order[, fields_names]]]])
 
     Call :meth:`search` and :meth:`read` at once.
     Useful for the client to reduce the number of calls.
 
-.. method:: ModelStorage.get_rec_name(ids, name)
-
-    Getter for the :class:`trytond.model.fields.Function` field
-    :attr:`rec_name`.
-
-.. method:: ModelStorage.search_rec_name(name, clause)
+.. classmethod:: ModelStorage.search_rec_name(name, clause)
 
     Searcher for the :class:`trytond.model.fields.Function` field
     :attr:`rec_name`.
 
-.. method:: ModelStorage.browse(ids)
+.. classmethod:: ModelStorage.browse(ids)
 
-    Return a :class:`BrowseRecordList` or a :class:`BrowseRecord` for the ``ids``.
+    Return a list of record instance for the ``ids``.
 
-.. method:: ModelStorage.export_data(ids, fields_names)
+.. classmethod:: ModelStorage.export_data(records, fields_names)
 
-    Return a list of list of values for each ``ids``.
+    Return a list of list of values for each ``records``.
     The list of values follows ``fields_names``.
     Relational fields are defined with ``/`` at any depth.
 
-.. method:: ModelStorage.import_data(fields_names, datas)
+.. classmethod:: ModelStorage.import_data(fields_names, data)
 
     Create records for all values in ``datas``.
     The field names of values must be defined in ``fields_names``.
     It returns a tuple containing: the number of records imported, the last values
     if failed, the exception if failed and the warning if failed.
 
-.. method:: ModelStorage.check_xml_record(ids, values)
+.. classmethod:: ModelStorage.check_xml_record(records, values)
 
-    Verify if the ids are originating from XML data. It is used to prevent
+    Verify if the records are originating from XML data. It is used to prevent
     modification of data coming from XML files. This method must be overiden to
     change this behavior.
 
-.. method:: ModelStorage.check_recursion(ids[, parent])
+.. classmethod:: ModelStorage.check_recursion(records[, parent])
 
     Helper method that checks if there is no recursion in the tree composed
     with ``parent`` as parent field name.
 
+Instance methods:
+
+.. method:: ModelStorage.get_rec_name(name)
+
+    Getter for the :class:`trytond.model.fields.Function` field
+    :attr:`rec_name`.
+
+.. method:: ModelStorage.save()
+
+    Save the modification made on the record instance.
+
 ========
 ModelSQL
 ========
@@ -367,18 +380,18 @@
 
     Like :attr:`Model._error_messages` but for :attr:`_sql_constraints`
 
-Instance methods:
+Class methods:
 
-.. method:: ModelSQL.default_sequence()
+.. classmethod:: ModelSQL.default_sequence()
 
     Return default value for sequence field if the model has one.
 
-.. method:: ModelSQL.table_query()
+.. classmethod:: ModelSQL.table_query()
 
     Could be overrided to use a custom SQL query instead of a table of the
     database. It should return a tuple containing SQL query and arguments.
 
-.. method:: ModelSQL.search_domain(domain[, active_test])
+.. classmethod:: ModelSQL.search_domain(domain[, active_test])
 
     Convert a :ref:`domain <topics-domain>` into a tuple containing:
 
@@ -427,8 +440,8 @@
 
 .. _singleton: http://en.wikipedia.org/wiki/Singleton_pattern
 
-Instance methods:
+Class methods:
 
-.. method:: ModelSingleton.get_singleton_id()
+.. classmethod:: ModelSingleton.get_singleton()
 
-    Return the id of the unique record if there is one.
+    Return the instance of the unique record if there is one.
diff -r fb51f5b4aa4c doc/ref/pool.rst
--- a/doc/ref/pool.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/ref/pool.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -13,32 +13,46 @@
 
 .. staticmethod:: Pool.register(klass[, type])
 
-Register a class of type (default: `model`).
+    Register a class of type (default: `model`).
 
 Class methods:
 
 .. classmethod:: Pool.start()
 
-Start the pool by registering all Tryton modules found.
+    Start the pool by registering all Tryton modules found.
 
 .. classmethod:: Pool.stop(database_name)
 
-Stop the pool by removing instances for the database.
+    Stop the pool by removing instances for the database.
 
 .. classmethod:: Pool.database_list()
 
-List all started database.
+    List all started database.
 
 Instance methods:
 
 .. method:: Pool.get(name[, type])
 
-Return the named instance of type from the pool.
+    Return the named instance of type from the pool.
 
 .. method:: Pool.object_name_list([type])
 
-Return the list of instances names.
+    Return the list of instances names.
 
 .. method:: Pool.iterobject([type])
 
-Return an interator over instances names.
+    Return an interator over instances names.
+
+.. method:: Pool.setup(module)
+
+    Setup classes for module and return a list of classes for each type in a
+    dictionary.
+
+========
+PoolMeta
+========
+
+.. class:: PoolMeta
+
+The PoolMeta is a metaclass helper to setup __name__ on class to be registered
+in the Pool.
diff -r fb51f5b4aa4c doc/ref/rpc.rst
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/ref/rpc.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -0,0 +1,24 @@
+.. _ref-rpc:
+.. module:: trytond.rpc
+
+===
+RPC
+===
+
+.. class:: RPC([readonly[, instantiate[, result]]])
+
+RPC is an object to define the behavior of Remote Procedur Call.
+
+Instance attributes are:
+
+.. attribute:: RPC.readonly
+
+    The transaction mode
+
+.. attribute:: RPC.instantiate
+
+    The position of the argument to be instanciated
+
+.. attribute:: RPC.result
+
+    The function to transform the result
diff -r fb51f5b4aa4c doc/ref/transaction.rst
--- a/doc/ref/transaction.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/ref/transaction.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -35,6 +35,10 @@
 
     The language code defines in the context.
 
+.. attribute:: Transaction.counter
+
+    Count the number of modification made in this transaction.
+
 .. method:: Transaction.start(database_name, user[, context])
 
     Start a new transaction and return a `context manager`_.
diff -r fb51f5b4aa4c doc/ref/wizard.rst
--- a/doc/ref/wizard.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/ref/wizard.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -12,7 +12,7 @@
 
 .. _`finite state machine`: http://en.wikipedia.org/wiki/Finite-state_machine
 
-.. class:: Wizard()
+.. class:: Wizard(session_id)
 
     This is the base for any wizard. It contains the engine for the finite
     state machine. A wizard must have some :class:`State` instance attributes
@@ -20,7 +20,7 @@
 
 Class attributes are:
 
-.. attribute:: Wizard._name
+.. attribute:: Wizard.__name__
 
     It contains the unique name to reference the wizard throughout the
     platform.
@@ -33,31 +33,39 @@
 
     It contains the name of the ending state.
 
-.. attribute:: Wizard._rpc
+.. attribute:: Wizard.__rpc__
 
-    Same as :attr:`trytond.model.Model._rpc`.
+    Same as :attr:`trytond.model.Model.__rpc__`.
 
 .. attribute:: Wizard.states
 
     It contains a dictionary with state name as key and :class:`State` as
     value.
 
-Instance methods are:
+Class methods are:
 
-.. method:: Wizard.init(module_name)
+.. classmethod:: Wizard.__setup__()
+
+    Setup the class before adding into the :class:`trytond.pool.Pool`.
+
+.. classmethod:: Wizard.__post_setup__()
+
+    Setup the class after added into the :class:`trytond.pool.Pool`.
+
+.. classmethod:: Wizard.__register__(module_name)
 
     Register the wizard.
 
-.. method:: Wizard.create()
+.. classmethod:: Wizard.create()
 
     Create a session for the wizard and returns a tuple containing the session
     id, the starting and ending state.
 
-.. method:: Wizard.delete(session_id)
+.. classmethod:: Wizard.delete(session_id)
 
     Delete the session.
 
-.. method:: Wizard.execute(session, data, state_name)
+.. classmethod:: Wizard.execute(session, data, state_name)
 
     Execute the wizard for the state.
     `session` can be an instance of :class:`Session` or a session id.
@@ -65,27 +73,6 @@
     `active_id`, `active_ids` and `active_model` must be set in the context
     according to the records on which the wizard is run.
 
-=======
-Session
-=======
-
-.. class:: Session(wizard, session_id)
-
-    A wizard session contains values of each :class:`StateView` associated to
-    the wizard.
-
-Instance attributes are:
-
-.. attribute:: Session.data
-
-    Raw storage of session data.
-
-Instance methods are:
-
-.. method:: Session.save()
-
-    Save the session in database.
-
 =====
 State
 =====
@@ -94,6 +81,12 @@
 
     This is the base for any wizard state.
 
+Instance attributes are:
+
+.. attribute:: State.name
+
+    The name of the state.
+
 =========
 StateView
 =========
@@ -120,7 +113,7 @@
 
 Instance methods are:
 
-.. method:: StateView.get_view
+.. method:: StateView.get_view()
 
     Returns the view definition like
     :meth:`~trytond.model.ModelView.fields_view_get`.
diff -r fb51f5b4aa4c doc/topics/models/index.rst
--- a/doc/topics/models/index.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/topics/models/index.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -5,8 +5,8 @@
 ======
 
 A model represents a single business logic or concept. It contains fields and
-defines the behaviors of the data. Most of the time, each model stores data in
-a single database table.
+defines the behaviors of the record. Most of the time, each model stores record
+in a single database table.
 
 The basics:
 
@@ -15,8 +15,11 @@
 
     * :ref:`Fields <ref-models-fields>` are defined as model attributes.
 
-    * Tryton generates the table definitions and provides an API to access the
-      data.
+    * Tryton generates the table definitions
+
+    * Tryton provides an API following the `active record pattern`_ to access the records.
+
+.. _active record pattern: http://en.wikipedia.org/wiki/Active_record
 
 Example
 =======
@@ -25,19 +28,18 @@
 fields::
 
     from trytond.model import ModelView, ModelSQL, fields
+    from trytond.pool import Pool
 
     class Party(ModelSQL, ModelView):
         "Party"
-        _description = __doc__
-        _name = "party.party"
+        __name__ = "party.party"
         name = fields.Char('Name')
         code = fields.Char('Code')
 
-    Party()
+    Pool.register(Party)
 
-Instantiating the class registers the model class in the framework.  Later the
-class will be instantiated once per database and stored in the
-:ref:`Pool <ref-pool>`.  Model instances are essentially accessors to
+The class must be registered in the :ref:`Pool <ref-pool>`.
+Model classes are essentially data mappers to records and Model instances are
 records.
 
 Model attributes define meta-information of the model. They are class
diff -r fb51f5b4aa4c doc/topics/modules/index.rst
--- a/doc/topics/modules/index.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/topics/modules/index.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -20,8 +20,8 @@
 ``__init__.py`` file
 --------------------
 
-It is the Python ``__init__.py`` to define a module. It must import all the
-Python files from the module.
+It is the Python ``__init__.py`` to define a module. It must contains a method
+named `register()` that must register to the pool all the objects of the module.
 
 
 ``tryton.cfg`` file
diff -r fb51f5b4aa4c doc/topics/reports/index.rst
--- a/doc/topics/reports/index.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/topics/reports/index.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -228,17 +228,21 @@
 
 ::
 
-  class InvoiceReport(Report):
-      _name = 'account.invoice'
-      def parse(self, report, objects, datas, localcontext):
-          employee_obj = Pool().get('company.employee')
-          employee = False
-          if Transaction().context.get('employee'):
-              employee = employee_obj.browse(Transaction().context['employee'])
-          localcontext['employee'] = employee
-          return super(InvoiceReport, self).parse(report, objects, datas,
-                   localcontext)
-  InvoiceReport()
+    from trytond.report import Report
+    from tryton.pool import Pool
+
+    class InvoiceReport(Report):
+        __name__ = 'account.invoice'
+
+        def parse(self, report, objects, datas, localcontext):
+            employee_obj = Pool().get('company.employee')
+            employee = False
+            if Transaction().context.get('employee'):
+                employee = employee_obj.browse(Transaction().context['employee'])
+            localcontext['employee'] = employee
+            return super(InvoiceReport, self).parse(report, objects, datas,
+                     localcontext)
+    Pool.register(InvoiceReport, type_='report')
 
 
 Replacing existing Tryton styles
diff -r fb51f5b4aa4c doc/topics/wizard.rst
--- a/doc/topics/wizard.rst	Mon Sep 24 11:36:39 2012 +0200
+++ b/doc/topics/wizard.rst	Mon Sep 24 20:36:36 2012 +0200
@@ -5,7 +5,7 @@
 ======
 
 A wizard describes a series of steps defined as :class:`trytond.wizard.State`.
-The wizard uses a :class:`trytond.wizard.Session` to store data between states.
+The wizard stores data in `ir.session.wizard` between states.
 
 The basics:
 
@@ -18,13 +18,18 @@
 Example
 =======
 
-This example defines a wizard which export translations::
+This example defines a wizard which export translations
+
+.. highlight:: python
+
+::
 
     from trytond.wizard import Wizard, StateView, StateTransition, Button
+    from trytond.pool import Pool
 
     class TranslationExport(Wizard):
         "Export translation"
-        _name = "ir.translation.export"
+        __name__ = "ir.translation.export"
 
         start = StateView('ir.translation.export.start',
             'ir.translation_export_start_view_form', [
@@ -37,21 +42,19 @@
                 Button('Close', 'end', 'tryton-close'),
                 ])
 
-        def transition_export(self, session):
+        def transition_export(self):
             pool = Pool()
             translation_obj = pool.get('ir.translation')
             file_data = translation_obj.translation_export(
-                session.start.language.code, session.start.module.name)
-            session.result.file = buffer(file_data)
+                self.start.language.code, self.start.module.name)
+            self.result.file = buffer(file_data)
             return 'result'
 
-        def default_result(self, session, fields):
+        def default_result(self, fields):
             return {
-                'file': session.result.file,
+                'file': self.result.file,
                 }
 
-    TranslationExport()
+    Pool.register(TranslationExport, type_='wizard')
 
-Instantiating the class registers the wizard class in the framework. Later the
-class will be instantiated once per database and stored in the
-:ref:`Pool <topics-pool>`.
+The class must be registered in the :ref:`Pool <ref-pool>`.
diff -r fb51f5b4aa4c trytond/backend/database.py
--- a/trytond/backend/database.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/backend/database.py	Mon Sep 24 20:36:36 2012 +0200
@@ -148,13 +148,15 @@
         '''
         Commit the cursor
         '''
-        self.cache = {}
+        for cache in self.cache.itervalues():
+            cache.clear()
 
     def rollback(self):
         '''
         Rollback the cursor
         '''
-        self.cache = {}
+        for cache in self.cache.itervalues():
+            cache.clear()
 
     def test(self):
         '''
diff -r fb51f5b4aa4c trytond/backend/postgresql/table.py
--- a/trytond/backend/postgresql/table.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/backend/postgresql/table.py	Mon Sep 24 20:36:36 2012 +0200
@@ -24,8 +24,9 @@
         if not self.table_exist(self.cursor, self.table_name):
             self.cursor.execute('CREATE TABLE "%s" ()' % self.table_name)
 
-        self.cursor.execute('COMMENT ON TABLE "%s" IS \'%s\'' %
-                (self.table_name, model._description.replace("'", "''")))
+        if model.__doc__:
+            self.cursor.execute('COMMENT ON TABLE "%s" IS \'%s\'' %
+                (self.table_name, model.__doc__.replace("'", "''")))
 
         self._update_definitions()
         if 'id' not in self._columns:
diff -r fb51f5b4aa4c trytond/backend/table.py
--- a/trytond/backend/table.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/backend/table.py	Mon Sep 24 20:36:36 2012 +0200
@@ -20,7 +20,7 @@
             self.table_name = model._table + '__history'
         else:
             self.table_name = model._table
-        self.object_name = model._name
+        self.object_name = model.__name__
         if history:
             self.sequence_name = self.table_name + '___id_seq'
         else:
diff -r fb51f5b4aa4c trytond/cache.py
--- a/trytond/cache.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/cache.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,7 +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 inspect
-import time
 import datetime
 from threading import Lock
 from trytond.transaction import Transaction
@@ -12,101 +10,51 @@
 
 class Cache(object):
     """
-    Use it as a decorator of the function you plan to cache
-    Timeout: 0 = no timeout, otherwise in seconds
+    A key value LRU cache with size limit.
     """
     _cache_instance = []
     _resets = {}
     _resets_lock = Lock()
 
-    def __init__(self, name, timeout=3600, max_len=1024):
-        self.timeout = timeout
-        self.max_len = max_len
+    def __init__(self, name, size_limit=1024, context=True):
+        self.size_limit = size_limit
+        self.context = context
         self._cache = {}
         self._cache_instance.append(self)
         self._name = name
         self._timestamp = None
         self._lock = Lock()
 
-    def __call__(self, function):
-        arg_names = inspect.getargspec(function)[0]
+    def _key(self, key):
+        if self.context:
+            return (key, Transaction().user, repr(Transaction.context))
+        return key
 
-        def reset():
-            dbname = Transaction().cursor.dbname
-            Cache.reset(dbname, self._name)
-            self._lock.acquire()
+    def get(self, key, default=None):
+        cursor = Transaction().cursor
+        key = self._key(key)
+        with self._lock:
+            cache = self._cache.setdefault(cursor.dbname,
+                LRUDict(self.size_limit))
             try:
-                self._cache[dbname] = {}
-            finally:
-                self._lock.release()
-            return True
+                result = cache[key] = cache.pop(key)
+                return result
+            except KeyError:
+                return default
 
-        def call(model, *args, **kwargs):
-            result = None
-            find = False
-            cursor = Transaction().cursor
-            # Update named arguments with positional argument values
-            kwargs_origin = kwargs.copy()
-            kwargs.update(dict(zip(arg_names, args)))
-            kwargs = kwargs.items()
-            kwargs.sort()
+    def set(self, key, value):
+        cursor = Transaction().cursor
+        key = self._key(key)
+        with self._lock:
+            cache = self._cache.setdefault(cursor.dbname,
+                LRUDict(self.size_limit))
+            cache[key] = value
 
-            self._lock.acquire()
-            try:
-                self._cache.setdefault(cursor.dbname, {})
-            finally:
-                self._lock.release()
-
-            lower = None
-            self._lock.acquire()
-            try:
-                if len(self._cache[cursor.dbname]) > self.max_len:
-                    mintime = time.time() - self.timeout
-                    for key in self._cache[cursor.dbname].keys():
-                        last_time = self._cache[cursor.dbname][key][1]
-                        if mintime > last_time:
-                            del self._cache[cursor.dbname][key]
-                        else:
-                            if not lower or lower[1] > last_time:
-                                lower = (key, last_time)
-                if (len(self._cache[cursor.dbname]) > self.max_len
-                        and lower):
-                    del self._cache[cursor.dbname][lower[0]]
-            finally:
-                self._lock.release()
-
-            # Work out key as a tuple
-            key = (id(model), Transaction().user, repr(Transaction().context),
-                    repr(kwargs))
-
-            # Check cache and return cached value if possible
-            self._lock.acquire()
-            try:
-                if key in self._cache[cursor.dbname]:
-                    (value, last_time) = self._cache[cursor.dbname][key]
-                    mintime = time.time() - self.timeout
-                    if self.timeout <= 0 or mintime <= last_time:
-                        self._cache[cursor.dbname][key] = (value,
-                                time.time())
-                        result = value
-                        find = True
-            finally:
-                self._lock.release()
-
-            if not find:
-                # Work out new value, cache it and return it
-                result = function(model, *args, **kwargs_origin)
-
-                self._lock.acquire()
-                try:
-                    self._cache[cursor.dbname][key] = (result, time.time())
-                finally:
-                    self._lock.release()
-            return result
-
-        call.__doc__ = function.__doc__
-        call.reset = reset
-        return call
+    def clear(self):
+        cursor = Transaction().cursor
+        Cache.reset(cursor.dbname, self._name)
+        with self._lock:
+            self._cache[cursor.dbname] = LRUDict(self.size_limit)
 
     @staticmethod
     def clean(dbname):
@@ -122,28 +70,21 @@
         finally:
             cursor.commit()
             cursor.close()
-        for obj in Cache._cache_instance:
-            if obj._name in timestamps:
-                if (not obj._timestamp
-                        or timestamps[obj._name] > obj._timestamp):
-                    obj._timestamp = timestamps[obj._name]
-                    obj._lock.acquire()
-                    try:
-                        obj._cache[dbname] = {}
-                    finally:
-                        obj._lock.release()
+        for inst in Cache._cache_instance:
+            if inst._name in timestamps:
+                with inst._lock:
+                    if (not inst._timestamp
+                            or timestamps[inst._name] > inst._timestamp):
+                        inst._timestamp = timestamps[inst._name]
+                        inst._cache[dbname] = LRUDict(inst.size_limit)
 
     @staticmethod
     def reset(dbname, name):
         if not CONFIG['multi_server']:
             return
-        Cache._resets_lock.acquire()
-        try:
+        with Cache._resets_lock:
             Cache._resets.setdefault(dbname, set())
             Cache._resets[dbname].add(name)
-        finally:
-            Cache._resets_lock.release()
-        return
 
     @staticmethod
     def resets(dbname):
@@ -151,24 +92,24 @@
             return
         database = Database(dbname).connect()
         cursor = database.cursor()
-        Cache._resets_lock.acquire()
-        Cache._resets.setdefault(dbname, set())
         try:
-            for name in Cache._resets[dbname]:
-                cursor.execute('SELECT name FROM ir_cache WHERE name = %s',
-                            (name,))
-                if cursor.fetchone():
-                    cursor.execute('UPDATE ir_cache SET "timestamp" = %s '\
+            with Cache._resets_lock:
+                Cache._resets.setdefault(dbname, set())
+                for name in Cache._resets[dbname]:
+                    cursor.execute('SELECT name FROM ir_cache WHERE name = %s',
+                        (name,))
+                    if cursor.fetchone():
+                        # It would be better to insert only
+                        cursor.execute('UPDATE ir_cache SET "timestamp" = %s '
                             'WHERE name = %s', (datetime.datetime.now(), name))
-                else:
-                    cursor.execute('INSERT INTO ir_cache ' \
-                            '("timestamp", "name") ' \
+                    else:
+                        cursor.execute('INSERT INTO ir_cache '
+                            '("timestamp", "name") '
                             'VALUES (%s, %s)', (datetime.datetime.now(), name))
-            Cache._resets[dbname].clear()
+                Cache._resets[dbname].clear()
         finally:
             cursor.commit()
             cursor.close()
-            Cache._resets_lock.release()
 
 
 class LRUDict(OrderedDict):
diff -r fb51f5b4aa4c trytond/convert.py
--- a/trytond/convert.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/convert.py	Mon Sep 24 20:36:36 2012 +0200
@@ -191,11 +191,11 @@
             eval_attr = attributes.get('eval', '')
 
             if search_attr:
-                search_model = self.model._columns[field_name].model_name
-                f_obj = self.mh.pool.get(search_model)
+                search_model = self.model._fields[field_name].model_name
+                SearchModel = self.mh.pool.get(search_model)
                 with Transaction().set_context(active_test=False):
-                    self.values[field_name], = \
-                            f_obj.search(safe_eval(search_attr))
+                    found, = SearchModel.search(safe_eval(search_attr))
+                    self.values[field_name] = found.id
 
             elif ref_attr:
                 self.values[field_name] = self.mh.get_id(ref_attr)
@@ -255,7 +255,8 @@
         elif name == "record":
             if self.xml_id in self.xml_ids and not self.update:
                 raise Exception('Duplicate id: "%s".' % (self.xml_id,))
-            self.mh.import_record(self.model._name, self.values, self.xml_id)
+            self.mh.import_record(
+                self.model.__name__, self.values, self.xml_id)
             self.xml_ids.append(self.xml_id)
             return None
         else:
@@ -263,7 +264,7 @@
 
     def current_state(self):
         return "In tag record: model %s with id %s." % \
-               (self.model and self.model._name or "?", self.xml_id)
+               (self.model and self.model.__name__ or "?", self.xml_id)
 
 
 # Custom exception:
@@ -280,10 +281,10 @@
     Provide some helper function to ease cache access and management.
     """
 
-    def __init__(self, modeldata_obj, pool):
+    def __init__(self, ModelData, pool):
         self.fs2db = {}
         self.fetched_modules = []
-        self.modeldata_obj = modeldata_obj
+        self.ModelData = ModelData
         self.browserecord = {}
         self.pool = pool
 
@@ -317,15 +318,10 @@
         if module not in self.fetched_modules:
             return
         self.browserecord[module].setdefault(model_name, {})
-        model_obj = self.pool.get(model_name)
+        Model = self.pool.get(model_name)
         if not ids:
-            object_ids = self.browserecord[module][model_name].keys()
-        else:
-            if isinstance(ids, (int, long)):
-                object_ids = [ids]
-            else:
-                object_ids = ids
-        models = model_obj.browse(object_ids)
+            ids = self.browserecord[module][model_name].keys()
+        models = Model.browse(ids)
         for model in models:
             if model.id in self.browserecord[module][model_name]:
                 for cache in Transaction().cursor.cache.values():
@@ -339,13 +335,13 @@
     def fetch_new_module(self, module):
         cursor = Transaction().cursor
         self.fs2db[module] = {}
-        module_data_ids = self.modeldata_obj.search([
+        module_data_ids = self.ModelData.search([
                 ('module', '=', module),
                 ('inherit', '=', None),
                 ], order=[('db_id', 'ASC')])
 
         record_ids = {}
-        for rec in self.modeldata_obj.browse(module_data_ids):
+        for rec in self.ModelData.browse(module_data_ids):
             self.fs2db[rec.module][rec.fs_id] = {
                 "db_id": rec.db_id, "model": rec.model,
                 "id": rec.id, "values": rec.values
@@ -359,16 +355,16 @@
         for model_name in record_ids.keys():
             if model_name not in object_name_list:
                 continue
-            model_obj = self.pool.get(model_name)
+            Model = self.pool.get(model_name)
             self.browserecord[module][model_name] = {}
             for i in range(0, len(record_ids[model_name]), cursor.IN_MAX):
                 sub_record_ids = record_ids[model_name][i:i + cursor.IN_MAX]
                 with Transaction().set_context(active_test=False):
-                    ids = model_obj.search([
+                    records = Model.search([
                         ('id', 'in', sub_record_ids),
                         ])
                 with Transaction().set_context(language='en_US'):
-                    models = model_obj.browse(ids)
+                    models = Model.browse(map(int, records))
                 for model in models:
                     self.browserecord[module][model_name][model.id] = model
         self.fetched_modules.append(module)
@@ -382,8 +378,8 @@
 
         self.pool = pool
         self.module = module
-        self.modeldata_obj = pool.get('ir.model.data')
-        self.fs2db = Fs2bdAccessor(self.modeldata_obj, pool)
+        self.ModelData = pool.get('ir.model.data')
+        self.fs2db = Fs2bdAccessor(self.ModelData, pool)
         self.to_delete = self.populate_to_delete()
         self.noupdate = None
         self.skip_data = False
@@ -481,26 +477,28 @@
         return self.fs2db.get(module, xml_id)["db_id"]
 
     @staticmethod
-    def _clean_value(key, browse_record, object_ref):
+    def _clean_value(key, record):
         """
         Take a field name, a browse_record, and a reference to the
         corresponding object.  Return a raw value has it must look on the
         db.
         """
-
+        Model = record.__class__
         # search the field type in the object or in a parent
-        if key in object_ref._columns:
-            field_type = object_ref._columns[key]._type
+        if key in Model._fields:
+            field_type = Model._fields[key]._type
         else:
-            field_type = object_ref._inherit_fields[key][2]._type
+            field_type = Model._inherit_fields[key][2]._type
 
         # handle the value regarding to the type
         if field_type == 'many2one':
-            return browse_record[key] and browse_record[key].id or None
+            return getattr(record, key).id if getattr(record, key) else None
         elif field_type == 'reference':
-            if not browse_record[key]:
+            if not getattr(record, key):
                 return None
-            ref_mode, ref_id = browse_record[key].split(',', 1)
+            elif not isinstance(getattr(record, key), basestring):
+                return str(getattr(record, key))
+            ref_mode, ref_id = getattr(record, key).split(',', 1)
             try:
                 ref_id = safe_eval(ref_id)
             except Exception:
@@ -511,7 +509,7 @@
         elif field_type in ['one2many', 'many2many']:
             raise Unhandled_field("Unhandled field %s" % key)
         else:
-            return browse_record[key]
+            return getattr(record, key)
 
     def populate_to_delete(self):
         """Create a list of all the records that whe should met in the update
@@ -520,12 +518,11 @@
 
         # Fetch the data in id descending order to avoid depedendcy
         # problem when the corresponding recordds will be deleted:
-        module_data_ids = self.modeldata_obj.search([
+        module_data = self.ModelData.search([
                 ('module', '=', self.module),
                 ('inherit', '=', None),
                 ], order=[('id', 'DESC')])
-        return set(rec.fs_id for rec in self.modeldata_obj.browse(
-            module_data_ids))
+        return set(rec.fs_id for rec in module_data)
 
     def import_record(self, model, values, fs_id):
         module = self.module
@@ -544,7 +541,7 @@
                 raise Exception('Reference to %s.%s not found' % \
                         (module, fs_id))
 
-        object_ref = self.pool.get(model)
+        Model = self.pool.get(model)
 
         if self.fs2db.get(module, fs_id):
 
@@ -581,62 +578,58 @@
                 raise Exception("This record try to overwrite " \
                 "data with the wrong model: %s (module: %s)" % (fs_id, module))
 
+            record = self.fs2db.get_browserecord(module, Model.__name__, db_id)
             #Re-create record if it was deleted
-            if not self.fs2db.get_browserecord(
-                    module, object_ref._name, db_id):
+            if not record:
                 with Transaction().set_context(
                         module=module, language='en_US'):
-                    db_id = object_ref.create(values)
+                    record = Model.create(values)
 
                 # reset_browsercord
-                self.fs2db.reset_browsercord(module, object_ref._name, db_id)
+                self.fs2db.reset_browsercord(
+                    module, Model.__name__, [record.id])
 
-                record = self.fs2db.get_browserecord(module, object_ref._name,
-                        db_id)
+                record = self.fs2db.get_browserecord(
+                    module, Model.__name__, record.id)
                 for table, field_name, field in \
-                        object_ref._inherit_fields.values():
-                    inherit_db_ids[table] = record[field_name].id
+                        Model._inherit_fields.values():
+                    inherit_db_ids[table] = getattr(record, field_name).id
 
                 for table in inherit_db_ids.keys():
-                    data_id = self.modeldata_obj.search([
+                    data = self.ModelData.search([
                         ('fs_id', '=', fs_id),
                         ('module', '=', module),
                         ('model', '=', table),
                         ], limit=1)
-                    if data_id:
-                        self.modeldata_obj.write(data_id, {
+                    if data:
+                        self.ModelData.write(data, {
                             'db_id': inherit_db_ids[table],
                             'inherit': True,
                             })
                     else:
-                        data_id = self.modeldata_obj.create({
+                        data = self.ModelData.create({
                             'fs_id': fs_id,
                             'module': module,
                             'model': table,
                             'db_id': inherit_db_ids[table],
                             'inherit': True,
                             })
-                    inherit_mdata_ids.append((table, data_id))
+                    inherit_mdata_ids.append((table, data.id))
 
-                data_id = self.modeldata_obj.search([
+                data = self.ModelData.search([
                     ('fs_id', '=', fs_id),
                     ('module', '=', module),
-                    ('model', '=', object_ref._name),
-                    ], limit=1)[0]
-                self.modeldata_obj.write(data_id, {
-                    'db_id': db_id,
+                    ('model', '=', Model.__name__),
+                    ], limit=1)
+                self.ModelData.write(data, {
+                    'db_id': record.id,
                     })
-                self.fs2db.get(module, fs_id)["db_id"] = db_id
-
-            db_val = self.fs2db.get_browserecord(module, object_ref._name,
-                    db_id)
-            if not db_val:
-                db_val = object_ref.browse(db_id)
+                self.fs2db.get(module, fs_id)["db_id"] = record.id
 
             to_update = {}
             for key in values:
 
-                db_field = self._clean_value(key, db_val, object_ref)
+                db_field = self._clean_value(key, record)
 
                 # if the fs value is the same has in the db, whe ignore it
                 val = values[key]
@@ -648,22 +641,22 @@
 
                 # we cannot update a field if it was changed by a user...
                 if key not in old_values:
-                    if key in object_ref._columns:
-                        expected_value = object_ref._defaults.get(
+                    if key in Model._fields:
+                        expected_value = Model._defaults.get(
                             key, lambda *a: None)()
                     else:
-                        inherit_obj = self.pool.get(
-                            object_ref._inherit_fields[key][0])
-                        expected_value = inherit_obj._defaults.get(
+                        Inherit = self.pool.get(
+                            Model._inherit_fields[key][0])
+                        expected_value = Inherit._defaults.get(
                             key, lambda *a: None)()
                 else:
                     expected_value = old_values[key]
 
                 # Migration from 2.0: Reference field change value
-                if key in object_ref._columns:
-                    field_type = object_ref._columns[key]._type
+                if key in Model._fields:
+                    field_type = Model._fields[key]._type
                 else:
-                    field_type = object_ref._inherit_fields[key][2]._type
+                    field_type = Model._inherit_fields[key][2]._type
                 if field_type == 'reference':
                     if (expected_value and expected_value.endswith(',0')
                             and not db_field):
@@ -677,7 +670,7 @@
                     logging.getLogger("convert").warning(
                         "Field %s of %s@%s not updated (id: %s), because "\
                         "it has changed since the last update" % \
-                        (key, db_id, model, fs_id))
+                        (key, record.id, model, fs_id))
                     continue
 
                 # so, the field in the fs and in the db are different,
@@ -689,32 +682,32 @@
                 # write the values in the db:
                 with Transaction().set_context(
                         module=module, language='en_US'):
-                    object_ref.write(db_id, to_update)
-                self.fs2db.reset_browsercord(module, object_ref._name, db_id)
+                    Model.write([record], to_update)
+                self.fs2db.reset_browsercord(
+                    module, Model.__name__, [record.id])
 
             if not inherit_db_ids:
-                record = object_ref.browse(db_id)
                 for table, field_name, field in \
-                        object_ref._inherit_fields.values():
-                    inherit_db_ids[table] = record[field_name].id
+                        Model._inherit_fields.values():
+                    inherit_db_ids[table] = getattr(record, field_name).id
             if not inherit_mdata_ids:
                 for table in inherit_db_ids.keys():
-                    data_id = self.modeldata_obj.search([
+                    data, = self.ModelData.search([
                         ('fs_id', '=', fs_id),
                         ('module', '=', module),
                         ('model', '=', table),
                         ], limit=1)
-                    inherit_mdata_ids.append((table, data_id))
+                    inherit_mdata_ids.append((table, data.id))
 
             if to_update:
                 # re-read it: this ensure that we store the real value
                 # in the model_data table:
-                db_val = self.fs2db.get_browserecord(module, object_ref._name,
-                        db_id)
-                if not db_val:
-                    db_val = object_ref.browse(db_id)
+                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, db_val, object_ref)
+                    values[key] = self._clean_value(key, record)
 
             if module != self.module:
                 temp_values = old_values.copy()
@@ -722,16 +715,17 @@
                 values = temp_values
 
             if values != old_values:
-                self.modeldata_obj.write(mdata_id, {
+                self.ModelData.write([self.ModelData(mdata_id)], {
                     'fs_id': fs_id,
                     'model': model,
                     'module': module,
-                    'db_id': db_id,
+                    'db_id': record.id,
                     'values': str(values),
                     'date_update': datetime.datetime.now(),
                     })
                 for table, inherit_mdata_id in inherit_mdata_ids:
-                    self.modeldata_obj.write(inherit_mdata_id, {
+                    self.ModelData.write(
+                        [self.ModelData(inherit_mdata_id)], {
                         'fs_id': fs_id,
                         'model': table,
                         'module': module,
@@ -740,27 +734,26 @@
                         'date_update': datetime.datetime.now(),
                         })
             # reset_browsercord to keep cache memory low
-            self.fs2db.reset_browsercord(module, object_ref._name, db_id)
+            self.fs2db.reset_browsercord(module, Model.__name__, [record.id])
 
         else:
             # this record is new, create it in the db:
             with Transaction().set_context(module=module, language='en_US'):
-                db_id = object_ref.create(values)
+                record = Model.create(values)
             inherit_db_ids = {}
 
-            record = object_ref.browse(db_id)
             for table, field_name, field in (
-                    object_ref._inherit_fields.values()):
-                inherit_db_ids[table] = record[field_name].id
+                    Model._inherit_fields.values()):
+                inherit_db_ids[table] = getattr(record, field_name).id
 
             # re-read it: this ensure that we store the real value
             # in the model_data table:
-            db_val = object_ref.browse(db_id)
+            record = Model(record.id)
             for key in values:
-                values[key] = self._clean_value(key, db_val, object_ref)
+                values[key] = self._clean_value(key, record)
 
             for table in inherit_db_ids.keys():
-                self.modeldata_obj.create({
+                self.ModelData.create({
                     'fs_id': fs_id,
                     'model': table,
                     'module': module,
@@ -770,20 +763,20 @@
                     'noupdate': self.noupdate,
                     })
 
-            mdata_id = self.modeldata_obj.create({
+            mdata = self.ModelData.create({
                 'fs_id': fs_id,
                 'model': model,
                 'module': module,
-                'db_id': db_id,
+                'db_id': record.id,
                 'values': str(values),
                 'noupdate': self.noupdate,
                 })
 
             # update fs2db:
             self.fs2db.set(module, fs_id, {
-                    "db_id": db_id, "model": model,
-                    "id": mdata_id, "values": str(values)})
-            self.fs2db.reset_browsercord(module, model, db_id)
+                    "db_id": record.id, "model": model,
+                    "id": mdata.id, "values": str(values)})
+            self.fs2db.reset_browsercord(module, model, [record.id])
 
 
 def post_import(pool, module, to_delete):
@@ -792,26 +785,26 @@
     """
     cursor = Transaction().cursor
     mdata_delete = []
-    modeldata_obj = pool.get("ir.model.data")
+    ModelData = pool.get("ir.model.data")
 
     with Transaction().set_context(active_test=False):
-        mdata_ids = modeldata_obj.search([
+        mdata = ModelData.search([
             ('fs_id', 'in', to_delete),
             ('module', '=', module),
             ], order=[('id', 'DESC')])
 
     object_name_list = set(pool.object_name_list())
-    for mrec in modeldata_obj.browse(mdata_ids):
-        mdata_id, model, db_id = mrec.id, mrec.model, mrec.db_id
+    for mrec in mdata:
+        model, db_id = mrec.model, mrec.db_id
 
         logging.getLogger("convert").info(
                 'Deleting %s@%s' % (db_id, model))
         try:
             # Deletion of the record
             if model in object_name_list:
-                model_obj = pool.get(model)
-                model_obj.delete(db_id)
-                mdata_delete.append(mdata_id)
+                Model = pool.get(model)
+                Model.delete([Model(db_id)])
+                mdata_delete.append(mrec)
             else:
                 logging.getLogger("convert").warning(
                         'Could not delete id %d of model %s because model no '
@@ -831,7 +824,7 @@
 
     # Clean model_data:
     if mdata_delete:
-        modeldata_obj.delete(mdata_delete)
+        ModelData.delete(mdata_delete)
         cursor.commit()
 
     return True
diff -r fb51f5b4aa4c trytond/error.py
--- a/trytond/error.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/error.py	Mon Sep 24 20:36:36 2012 +0200
@@ -7,7 +7,8 @@
 
 class WarningErrorMixin(object):
 
-    def raise_user_error(self, error, error_args=None,
+    @classmethod
+    def raise_user_error(cls, error, error_args=None,
             error_description='', error_description_args=None,
             raise_exception=True):
         '''
@@ -26,14 +27,14 @@
             (or tuple if error_description is not empty) instead of raising an
             exception.
         '''
-        translation_obj = Pool().get('ir.translation')
+        Translation = Pool().get('ir.translation')
 
-        error = self._error_messages.get(error, error)
+        error = cls._error_messages.get(error, error)
 
         language = Transaction().language
-        res = translation_obj.get_source(self._name, 'error', language, error)
+        res = Translation.get_source(cls.__name__, 'error', language, error)
         if not res:
-            res = translation_obj.get_source(error, 'error', language)
+            res = Translation.get_source(error, 'error', language)
         if res:
             error = res
 
@@ -44,13 +45,13 @@
                 pass
 
         if error_description:
-            error_description = self._error_messages.get(error_description,
+            error_description = cls._error_messages.get(error_description,
                     error_description)
 
-            res = translation_obj.get_source(self._name, 'error', language,
-                    error_description)
+            res = Translation.get_source(cls.__name__, 'error', language,
+                error_description)
             if not res:
-                res = translation_obj.get_source(error_description, 'error',
+                res = Translation.get_source(error_description, 'error',
                     language)
             if res:
                 error_description = res
@@ -70,7 +71,8 @@
         else:
             return error
 
-    def raise_user_warning(self, warning_name, warning,
+    @classmethod
+    def raise_user_warning(cls, warning_name, warning,
             warning_args=None, warning_description='',
             warning_description_args=None):
         '''
@@ -87,16 +89,16 @@
         :param warning_description_args: the arguments that will be used
             for "%"-based substitution
         '''
-        warning_obj = Pool().get('res.user.warning')
-        if warning_obj.check(warning_name):
+        Warning_ = Pool().get('res.user.warning')
+        if Warning_.check(warning_name):
             if warning_description:
-                warning, warning_description = self.raise_user_error(warning,
+                warning, warning_description = cls.raise_user_error(warning,
                         error_args=warning_args,
                         error_description=warning_description,
                         error_description_args=warning_description_args,
                         raise_exception=False)
                 raise UserWarning(warning_name, warning, warning_description)
             else:
-                warning = self.raise_user_error(warning,
+                warning = cls.raise_user_error(warning,
                         error_args=warning_args, raise_exception=False)
                 raise UserWarning(warning_name, warning)
diff -r fb51f5b4aa4c trytond/ir/__init__.py
--- a/trytond/ir/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,8 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+from ..pool import Pool
 from .translation import *
 from .sequence import *
-from . import ui
+from .ui.menu import *
+from .ui.view import *
+from .ui.icon import *
 from .property import *
 from .action import *
 from .model import *
@@ -11,8 +14,81 @@
 from .lang import *
 from .export import *
 from .rule import *
-from . import module
+from .module import *
 from .cache import *
 from .date import *
 from .trigger import *
 from .session import *
+
+
+def register():
+    Pool.register(
+        Translation,
+        ReportTranslationSetStart,
+        ReportTranslationSetSucceed,
+        TranslationCleanStart,
+        TranslationCleanSucceed,
+        TranslationUpdateStart,
+        TranslationExportStart,
+        TranslationExportResult,
+        SequenceType,
+        Sequence,
+        SequenceStrict,
+        UIMenu,
+        View,
+        ShowViewStart,
+        ViewShortcut,
+        ViewTreeWidth,
+        ViewTreeExpandedState,
+        Icon,
+        Property,
+        Action,
+        ActionKeyword,
+        ActionReport,
+        ActionActWindow,
+        ActionActWindowView,
+        ActionWizard,
+        ActionURL,
+        Model,
+        ModelField,
+        ModelAccess,
+        ModelFieldAccess,
+        ModelButton,
+        ModelData,
+        PrintModelGraphStart,
+        Attachment,
+        Cron,
+        Lang,
+        Export,
+        ExportLine,
+        RuleGroup,
+        Rule,
+        Module,
+        ModuleDependency,
+        ModuleConfigWizardItem,
+        ModuleConfigWizardFirst,
+        ModuleConfigWizardOther,
+        ModuleInstallUpgradeStart,
+        ModuleInstallUpgradeDone,
+        Cache,
+        Date,
+        Trigger,
+        TriggerLog,
+        Session,
+        SessionWizard,
+        module='ir', type_='model')
+    Pool.register(
+        ReportTranslationSet,
+        TranslationClean,
+        TranslationUpdate,
+        TranslationExport,
+        OpenShortcut,
+        ShowView,
+        PrintModelGraph,
+        ModuleConfigWizard,
+        ModuleInstallUpgrade,
+        ModuleConfig,
+        module='ir', type_='wizard')
+    Pool.register(
+        ModelGraph,
+        module='ir', type_='report')
diff -r fb51f5b4aa4c trytond/ir/action.py
--- a/trytond/ir/action.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/action.py	Mon Sep 24 20:36:36 2012 +0200
@@ -5,21 +5,26 @@
 from operator import itemgetter
 from collections import defaultdict
 
-from ..model import ModelView, ModelSQL, ModelStorage, fields
+from ..model import ModelView, ModelSQL, fields
 from ..tools import file_open, safe_eval
 from ..backend import TableHandler
 from ..pyson import PYSONEncoder, CONTEXT, PYSON
 from ..transaction import Transaction
 from ..pool import Pool
 from ..cache import Cache
+from ..rpc import RPC
+
+__all__ = [
+    'Action', 'ActionKeyword', 'ActionReport', 'ActionActWindow',
+    'ActionActWindowView', 'ActionWizard', 'ActionURL',
+    ]
 
 EMAIL_REFKEYS = set(('cc', 'to', 'subject'))
 
 
 class Action(ModelSQL, ModelView):
     "Action"
-    _name = 'ir.action'
-    _description = __doc__
+    __name__ = 'ir.action'
     name = fields.Char('Name', required=True, translate=True)
     type = fields.Char('Type', required=True, readonly=True)
     usage = fields.Char('Usage')
@@ -30,22 +35,26 @@
     icon = fields.Many2One('ir.ui.icon', 'Icon')
     active = fields.Boolean('Active', select=True)
 
-    def __init__(self):
-        super(Action, self).__init__()
-        self._rpc.update({
-            'get_action_id': False,
-        })
+    @classmethod
+    def __setup__(cls):
+        super(Action, cls).__setup__()
+        cls.__rpc__.update({
+                'get_action_id': RPC(),
+                })
 
-    def default_usage(self):
+    @staticmethod
+    def default_usage():
         return None
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def get_action_id(self, action_id):
+    @classmethod
+    def get_action_id(cls, action_id):
         pool = Pool()
         with Transaction().set_context(active_test=False):
-            if self.search([
+            if cls.search([
                 ('id', '=', action_id),
                 ]):
                 return action_id
@@ -55,18 +64,18 @@
                     'ir.action.wizard',
                     'ir.action.url',
                     ):
-                action_obj = pool.get(action_type)
-                action_id2 = action_obj.search([
+                Action = pool.get(action_type)
+                action_id2 = Action.search([
                     ('id', '=', action_id),
                     ])
                 if action_id2:
-                    action = action_obj.browse(action_id2[0])
+                    action = Action.browse(action_id2[0])
                     return action.action.id
 
-    def get_action_values(self, type_, action_ids):
-        action_obj = Pool().get(type_)
-        columns = set(action_obj._columns.keys()
-            + action_obj._inherit_fields.keys())
+    @classmethod
+    def get_action_values(cls, type_, action_ids):
+        Action = Pool().get(type_)
+        columns = set(Action._fields.keys() + Action._inherit_fields.keys())
         columns.add('icon.rec_name')
         to_remove = ()
         if type_ == 'ir.action.report':
@@ -75,15 +84,12 @@
         elif type_ == 'ir.action.act_window':
             to_remove = ('domain', 'context', 'search_value')
         columns.difference_update(to_remove)
-        return action_obj.read(action_ids, list(columns))
-
-Action()
+        return Action.read(action_ids, list(columns))
 
 
 class ActionKeyword(ModelSQL, ModelView):
     "Action keyword"
-    _name = 'ir.action.keyword'
-    _description = __doc__
+    __name__ = 'ir.action.keyword'
     keyword = fields.Selection([
             ('tree_open', 'Open tree'),
             ('tree_action', 'Action tree'),
@@ -97,141 +103,93 @@
         ondelete='CASCADE', select=True)
     groups = fields.Function(fields.One2Many('res.group', None, 'Groups'),
         'get_groups', searcher='search_groups')
+    _get_keyword_cache = Cache('ir_action_keyword.get_keyword')
 
-    def __init__(self):
-        super(ActionKeyword, self).__init__()
-        self._rpc.update({'get_keyword': False})
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ActionKeyword, cls).__setup__()
+        cls.__rpc__.update({'get_keyword': RPC()})
+        cls._constraints += [
             ('check_wizard_model', 'wrong_wizard_model'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'wrong_wizard_model': 'Wrong wizard model!',
         })
 
-    def init(self, module_name):
-        super(ActionKeyword, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ActionKeyword, cls).__register__(module_name)
 
-        table = TableHandler(Transaction().cursor, self, module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action(['keyword', 'model'], 'add')
 
-    def get_groups(self, ids, name):
-        groups = {}
-        for keyword in self.browse(ids):
-            groups[keyword.id] = [g.id for g in keyword.action.groups]
-        return groups
+    def get_groups(self, name):
+        return [g.id for g in self.action.groups]
 
-    def search_groups(self, name, clause):
+    @classmethod
+    def search_groups(cls, name, clause):
         return [('action.groups',) + tuple(clause[1:])]
 
-    def check_wizard_model(self, ids):
-        pool = Pool()
-        action_wizard_obj = pool.get('ir.action.wizard')
-        for action_keyword in self.browse(ids):
-            if action_keyword.action.type == 'ir.action.wizard':
-                action_wizard_ids = action_wizard_obj.search([
-                    ('action', '=', action_keyword.action.id),
-                    ], limit=1)
-                if not action_wizard_ids:
-                    continue
-                action_wizard_id, = action_wizard_ids
-                action_wizard = action_wizard_obj.browse(action_wizard_id)
-                if action_wizard.model:
-                    model, record_id = action_keyword.model.split(',', 1)
-                    if model != action_wizard.model:
-                        return False
+    def check_wizard_model(self):
+        ActionWizard = Pool().get('ir.action.wizard')
+        if self.action.type == 'ir.action.wizard':
+            action_wizard, = ActionWizard.search([
+                ('action', '=', self.action.id),
+                ], limit=1)
+            if action_wizard.model:
+                if self.model.__name__ != action_wizard.model:
+                    return False
         return True
 
-    def _convert_vals(self, vals):
+    @staticmethod
+    def _convert_vals(vals):
         vals = vals.copy()
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
         if 'action' in vals:
-            vals['action'] = action_obj.get_action_id(vals['action'])
+            vals['action'] = Action.get_action_id(vals['action'])
         return vals
 
-    def models_get(self):
+    @staticmethod
+    def models_get():
         pool = Pool()
-        model_obj = pool.get('ir.model')
-        model_ids = model_obj.search([])
+        Model = pool.get('ir.model')
+        models = Model.search([])
         res = []
-        for model in model_obj.browse(model_ids):
+        for model in models:
             res.append([model.model, model.name])
         return res
 
-    def delete(self, ids):
-        pool = Pool()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for keyword in self.browse(ids):
-            # Restart the cache view
-            try:
-                pool.get(keyword.model.split(',')[0]
-                        ).fields_view_get.reset()
-            except Exception:
-                pass
-            try:
-                pool.get(keyword.model.split(',')[0]
-                    ).view_toolbar_get.reset()
-            except Exception:
-                pass
-        self.get_keyword.reset()
-        return super(ActionKeyword, self).delete(ids)
+    @classmethod
+    def delete(cls, keywords):
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
+        cls._get_keyword_cache.clear()
+        super(ActionKeyword, cls).delete(keywords)
 
-    def create(self, vals):
-        pool = Pool()
-        vals = self._convert_vals(vals)
-        if 'model' in vals:
-            # Restart the cache view
-            try:
-                pool.get(vals['model'].split(',')[0]
-                        ).fields_view_get.reset()
-            except Exception:
-                pass
-            try:
-                pool.get(vals['model'].split(',')[0]
-                    ).view_toolbar_get.reset()
-            except Exception:
-                pass
-        self.get_keyword.reset()
-        return super(ActionKeyword, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
+        cls._get_keyword_cache.clear()
+        vals = cls._convert_vals(vals)
+        return super(ActionKeyword, cls).create(vals)
 
-    def write(self, ids, vals):
-        pool = Pool()
-        vals = self._convert_vals(vals)
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for keyword in self.browse(ids):
-            # Restart the cache view
-            try:
-                pool.get(keyword.model.split(',')[0]
-                        ).fields_view_get.reset()
-            except Exception:
-                pass
-            try:
-                pool.get(keyword.model.split(',')[0]
-                    ).view_toolbar_get.reset()
-            except Exception:
-                pass
-        self.get_keyword.reset()
-        res = super(ActionKeyword, self).write(ids, vals)
-        for keyword in self.browse(ids):
-            # Restart the cache view
-            try:
-                pool.get(keyword.model.split(',')[0]
-                        ).fields_view_get.reset()
-            except Exception:
-                pass
-            try:
-                pool.get(keyword.model.split(',')[0]
-                    ).view_toolbar_get.reset()
-            except Exception:
-                pass
-        return res
+    @classmethod
+    def write(cls, keywords, vals):
+        vals = cls._convert_vals(vals)
+        super(ActionKeyword, cls).write(keywords, vals)
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
+        cls._get_keyword_cache.clear()
 
-    @Cache('ir_action_keyword.get_keyword')
-    def get_keyword(self, keyword, value):
-        pool = Pool()
-        action_obj = pool.get('ir.action')
+    @classmethod
+    def get_keyword(cls, keyword, value):
+        Action = Pool().get('ir.action')
+        key = (keyword, tuple(value))
+        keywords = cls._get_keyword_cache.get(key)
+        if keywords is not None:
+            return keywords
         keywords = []
         model, model_id = value
 
@@ -247,23 +205,21 @@
                     ('model', '=', model + ',' + str(model_id)),
                     ],
                 ]
-        action_keyword_ids = self.search(clause, order=[])
+        action_keywords = cls.search(clause, order=[])
         types = defaultdict(list)
-        for action_keyword in self.browse(action_keyword_ids):
+        for action_keyword in action_keywords:
             type_ = action_keyword.action.type
             types[type_].append(action_keyword.action.id)
         for type_, action_ids in types.iteritems():
-            keywords.extend(action_obj.get_action_values(type_, action_ids))
+            keywords.extend(Action.get_action_values(type_, action_ids))
         keywords.sort(key=itemgetter('name'))
+        cls._get_keyword_cache.set(key, keywords)
         return keywords
 
-ActionKeyword()
-
 
 class ActionReport(ModelSQL, ModelView):
     "Action report"
-    _name = 'ir.action.report'
-    _description = __doc__
+    __name__ = 'ir.action.report'
     _inherits = {'ir.action': 'action'}
     model = fields.Char('Model')
     report_name = fields.Char('Internal Name', required=True)
@@ -355,24 +311,26 @@
         "Example: {'to': 'test@example.com', 'cc': 'user@example.com'}")
     pyson_email = fields.Function(fields.Char('PySON Email'), 'get_pyson')
 
-    def __init__(self):
-        super(ActionReport, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ActionReport, cls).__setup__()
+        cls._sql_constraints += [
             ('report_name_module_uniq', 'UNIQUE(report_name, module)',
                 'The internal name must be unique by module!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_email', 'invalid_email'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
                 'invalid_email': 'Invalid email!',
                 })
 
-    def init(self, module_name):
-        super(ActionReport, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ActionReport, cls).__register__(module_name)
 
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 report_name_uniq has been removed
         table.drop_constraint('report_name_uniq')
@@ -380,16 +338,16 @@
         # Migration from 1.0 output_format (m2o) is now extension (selection)
         if table.column_exist('output_format'):
             cursor.execute(
-                'SELECT report.id FROM "' + self._table + '" report '
+                'SELECT report.id FROM "' + cls._table + '" report '
                 'JOIN ir_action_report_outputformat of '
                     'ON (report.output_format = of.id) '
                 'WHERE of.format = \'pdf\'')
 
             ids = [x[0] for x in cursor.fetchall()]
             with Transaction().set_user(0):
-                self.write(ids, {'extension': 'pdf'})
-                ids = self.search([('id', 'not in', ids)])
-                self.write(ids, {'extension': 'odt'})
+                cls.write(cls.browse(ids), {'extension': 'pdf'})
+                ids = cls.search([('id', 'not in', ids)])
+                cls.write(cls.browse(ids), {'extension': 'odt'})
 
             table.drop_column("output_format")
             TableHandler.dropTable(cursor, 'ir.action.report.outputformat',
@@ -397,7 +355,7 @@
 
         # Migrate from 2.0 remove required on extension
         table.not_null_action('extension', action='remove')
-        cursor.execute('UPDATE "' + self._table + '" '
+        cursor.execute('UPDATE "' + cls._table + '" '
             'SET extension = %s '
             'WHERE extension = %s', ('', 'odt'))
 
@@ -407,43 +365,50 @@
                 and table.column_exist('report_content_custom')):
             limit = cursor.IN_MAX
             cursor.execute('SELECT COUNT(id) '
-                'FROM "' + self._table + '"')
+                'FROM "' + cls._table + '"')
             report_count, = cursor.fetchone()
             for offset in range(0, report_count, limit):
                 cursor.execute(cursor.limit_clause(
                     'SELECT id, report_content_data '
-                    'FROM "' + self._table + '"'
+                    'FROM "' + cls._table + '"'
                     'ORDER BY id',
                     limit, offset))
                 for report_id, report in cursor.fetchall():
                     if report:
                         report = buffer(base64.decodestring(str(report)))
-                        cursor.execute('UPDATE "' + self._table + '" '
+                        cursor.execute('UPDATE "' + cls._table + '" '
                             'SET report_content_custom = %s '
                             'WHERE id = %s', (report, report_id))
             table.drop_column('report_content_data')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'ir.action.report'
 
-    def default_report_content(self):
+    @staticmethod
+    def default_report_content():
         return None
 
-    def default_direct_print(self):
+    @staticmethod
+    def default_direct_print():
         return False
 
-    def default_template_extension(self):
+    @staticmethod
+    def default_template_extension():
         return 'odt'
 
-    def default_extension(self):
+    @staticmethod
+    def default_extension():
         return ''
 
-    def default_module(self):
+    @staticmethod
+    def default_module():
         return Transaction().context.get('module') or ''
 
-    def check_email(self, ids):
+    @classmethod
+    def check_email(cls, reports):
         "Check email"
-        for report in self.browse(ids):
+        for report in reports:
             if report.email:
                 try:
                     value = safe_eval(report.email, CONTEXT)
@@ -457,134 +422,128 @@
                     return False
         return True
 
-    def get_report_content(self, ids, name):
-        res = {}
+    @classmethod
+    def get_report_content(cls, reports, name):
+        contents = {}
         converter = buffer
         default = None
-        format_ = Transaction().context.pop('%s.%s' % (self._name, name), '')
+        format_ = Transaction().context.pop('%s.%s'
+            % (cls.__name__, name), '')
         if format_ == 'size':
             converter = len
             default = 0
-        for report in self.browse(ids):
-            data = report[name + '_custom']
-            if not data and report[name[:-8]]:
+        for report in reports:
+            data = getattr(report, name + '_custom')
+            if not data and getattr(report, name[:-8]):
                 try:
-                    with file_open(report[name[:-8]].replace('/', os.sep),
+                    with file_open(
+                            getattr(report, name[:-8]).replace('/', os.sep),
                             mode='rb') as fp:
                         data = fp.read()
                 except Exception:
                     data = None
-            res[report.id] = converter(data) if data else default
-        return res
+            contents[report.id] = converter(data) if data else default
+        return contents
 
-    def set_report_content(self, ids, name, value):
-        self.write(ids, {'%s_custom' % name: value})
+    @classmethod
+    def set_report_content(cls, records, name, value):
+        cls.write(records, {'%s_custom' % name: value})
 
-    def get_style_content(self, ids, name):
-        res = {}
+    @classmethod
+    def get_style_content(cls, reports, name):
+        contents = {}
         converter = buffer
         default = None
-        format_ = Transaction().context.pop('%s.%s' % (self._name, name), '')
+        format_ = Transaction().context.pop('%s.%s'
+            % (cls.__name__, name), '')
         if format_ == 'size':
             converter = len
             default = 0
-        for report in self.browse(ids):
+        for report in reports:
             try:
                 with file_open(report.style.replace('/', os.sep),
                         mode='rb') as fp:
                     data = fp.read()
             except Exception:
                 data = None
-            res[report.id] = converter(data) if data else default
-        return res
+            contents[report.id] = converter(data) if data else default
+        return contents
 
-    def get_pyson(self, ids, name):
-        res = {}
+    @classmethod
+    def get_pyson(cls, reports, name):
+        pysons = {}
         encoder = PYSONEncoder()
         field = name[6:]
         defaults = {
             'email': '{}',
-        }
-        for act in self.browse(ids):
-            res[act.id] = encoder.encode(safe_eval(act[field] or
-                defaults.get(field, 'None'), CONTEXT))
-        return res
+            }
+        for report in reports:
+            pysons[report.id] = encoder.encode(safe_eval(getattr(report, field)
+                    or defaults.get(field, 'None'), CONTEXT))
+        return pysons
 
-    def copy(self, ids, default=None):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def copy(cls, reports, default=None):
+        Action = Pool().get('ir.action')
 
         if default is None:
             default = {}
         default = default.copy()
         default.setdefault('module', None)
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
-        new_ids = []
-        reports = self.browse(ids)
+        new_reports = []
         for report in reports:
             if report.report:
                 default['report_content'] = None
             default['report_name'] = report.report_name
-            default['action'] = action_obj.copy(report.action.id)
-            new_ids.append(super(ActionReport, self).copy(report.id,
-                default=default))
-        if int_id:
-            return new_ids[0]
-        return new_ids
+            default['action'] = Action.copy([report.action])[0].id
+            new_reports.extend(super(ActionReport, cls).copy([report],
+                    default=default))
+        return new_reports
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         later = {}
         vals = vals.copy()
         for field in vals:
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'set'):
+            if (field in cls._fields
+                    and hasattr(cls._fields[field], 'set')):
                 later[field] = vals[field]
         for field in later:
             del vals[field]
         cursor = Transaction().cursor
-        if cursor.nextid(self._table):
-            cursor.setnextid(self._table, cursor.currid('ir_action'))
-        new_id = super(ActionReport, self).create(vals)
-        report = self.browse(new_id)
+        if cursor.nextid(cls._table):
+            cursor.setnextid(cls._table, cursor.currid('ir_action'))
+        report = super(ActionReport, cls).create(vals)
         new_id = report.action.id
-        cursor.execute('UPDATE "' + self._table + '" SET id = %s ' \
+        cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \
                 'WHERE id = %s', (report.action.id, report.id))
-        cursor.update_auto_increment(self._table, report.action.id)
-        ModelStorage.delete(self, report.id)
-        self.write(new_id, later)
-        return new_id
+        cursor.update_auto_increment(cls._table, report.action.id)
+        report = cls(new_id)
+        cls.write([report], later)
+        return report
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, reports, vals):
         context = Transaction().context
         if 'module' in context:
             vals = vals.copy()
             vals['module'] = context['module']
 
-        return super(ActionReport, self).write(ids, vals)
+        super(ActionReport, cls).write(reports, vals)
 
-    def delete(self, ids):
-        pool = Pool()
-        action_obj = pool.get('ir.action')
+    @classmethod
+    def delete(cls, reports):
+        Action = Pool().get('ir.action')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        action_ids = [x.action.id for x in self.browse(ids)]
-
-        res = super(ActionReport, self).delete(ids)
-        action_obj.delete(action_ids)
-        return res
-
-ActionReport()
+        actions = [x.action for x in reports]
+        super(ActionReport, cls).delete(reports)
+        Action.delete(actions)
 
 
 class ActionActWindow(ModelSQL, ModelView):
     "Action act window"
-    _name = 'ir.action.act_window'
-    _description = __doc__
+    __name__ = 'ir.action.act_window'
     _inherits = {'ir.action': 'action'}
     domain = fields.Char('Domain Value')
     context = fields.Char('Context Value')
@@ -608,53 +567,62 @@
     pyson_search_value = fields.Function(fields.Char(
         'PySON Search Criteria'), 'get_pyson')
 
-    def __init__(self):
-        super(ActionActWindow, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ActionActWindow, cls).__setup__()
+        cls._constraints += [
             ('check_views', 'invalid_views'),
             ('check_domain', 'invalid_domain'),
             ('check_context', 'invalid_context'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_views': 'Invalid views!',
             'invalid_domain': 'Invalid domain or search criteria!',
             'invalid_context': 'Invalid context!',
         })
-        self._rpc.update({
-                'get': False,
+        cls.__rpc__.update({
+                'get': RPC(),
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        super(ActionActWindow, self).init(module_name)
+        super(ActionActWindow, cls).__register__(module_name)
 
         # Migration from 2.0: new search_value format
         cursor.execute('UPDATE "%s" '
             'SET search_value = %%s '
-            'WHERE search_value = %%s' % self._table,
+            'WHERE search_value = %%s' % cls._table,
             ('[]', '{}'))
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'ir.action.act_window'
 
-    def default_context(self):
+    @staticmethod
+    def default_context():
         return '{}'
 
-    def default_limit(self):
+    @staticmethod
+    def default_limit():
         return 0
 
-    def default_auto_refresh(self):
+    @staticmethod
+    def default_auto_refresh():
         return 0
 
-    def default_window_name(self):
+    @staticmethod
+    def default_window_name():
         return True
 
-    def default_search_value(self):
+    @staticmethod
+    def default_search_value():
         return '[]'
 
-    def check_views(self, ids):
+    @classmethod
+    def check_views(cls, actions):
         "Check views"
-        for action in self.browse(ids):
+        for action in actions:
             if action.res_model:
                 for act_window_view in action.act_window_views:
                     view = act_window_view.view
@@ -671,9 +639,10 @@
                         return False
         return True
 
-    def check_domain(self, ids):
+    @classmethod
+    def check_domain(cls, actions):
         "Check domain and search_value"
-        for action in self.browse(ids):
+        for action in actions:
             for domain in (action.domain, action.search_value):
                 if not domain:
                     continue
@@ -693,9 +662,10 @@
                         return False
         return True
 
-    def check_context(self, ids):
+    @classmethod
+    def check_context(cls, actions):
         "Check context"
-        for action in self.browse(ids):
+        for action in actions:
             if action.context:
                 try:
                     value = safe_eval(action.context, CONTEXT)
@@ -713,122 +683,105 @@
                         return False
         return True
 
-    def get_views(self, ids, name):
-        res = {}
-        for act in self.browse(ids):
-            res[act.id] = [(view.view.id, view.view.type)
-                    for view in act.act_window_views]
-        return res
+    def get_views(self, name):
+        return [(view.view.id, view.view.type)
+            for view in self.act_window_views]
 
-    def get_pyson(self, ids, name):
-        res = {}
+    @classmethod
+    def get_pyson(cls, windows, name):
+        pysons = {}
         encoder = PYSONEncoder()
         field = name[6:]
         defaults = {
             'domain': '[]',
             'context': '{}',
             'search_value': '{}',
-        }
-        for act in self.browse(ids):
-            res[act.id] = encoder.encode(safe_eval(act[field] or
-                defaults.get(field, 'None'), CONTEXT))
-        return res
+            }
+        for window in windows:
+            pysons[window.id] = encoder.encode(safe_eval(getattr(window, field)
+                    or defaults.get(field, 'None'), CONTEXT))
+        return pysons
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         later = {}
         vals = vals.copy()
         for field in vals:
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'set'):
+            if (field in cls._fields
+                    and hasattr(cls._fields[field], 'set')):
                 later[field] = vals[field]
         for field in later:
             del vals[field]
         cursor = Transaction().cursor
-        if cursor.nextid(self._table):
-            cursor.setnextid(self._table, cursor.currid('ir_action'))
-        new_id = super(ActionActWindow, self).create(vals)
-        act_window = self.browse(new_id)
+        if cursor.nextid(cls._table):
+            cursor.setnextid(cls._table, cursor.currid('ir_action'))
+        act_window = super(ActionActWindow, cls).create(vals)
         new_id = act_window.action.id
-        cursor.execute('UPDATE "' + self._table + '" SET id = %s ' \
+        cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \
                 'WHERE id = %s', (act_window.action.id, act_window.id))
-        cursor.update_auto_increment(self._table, act_window.action.id)
-        ModelStorage.delete(self, act_window.id)
-        self.write(new_id, later)
-        return new_id
+        cursor.update_auto_increment(cls._table, act_window.action.id)
+        act_window = cls(new_id)
+        cls.write([act_window], later)
+        return act_window
 
-    def delete(self, ids):
-        pool = Pool()
-        action_obj = pool.get('ir.action')
+    @classmethod
+    def delete(cls, act_windows):
+        Action = Pool().get('ir.action')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        action_ids = [x.action.id for x in self.browse(ids)]
+        actions = [x.action.id for x in act_windows]
+        super(ActionActWindow, cls).delete(act_windows)
+        Action.delete(actions)
 
-        res = super(ActionActWindow, self).delete(ids)
-        action_obj.delete(action_ids)
-        return res
-
-    def get(self, xml_id):
+    @classmethod
+    def get(cls, xml_id):
         'Get values from XML id'
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        action_obj = pool.get('ir.action')
-        action_id = model_data_obj.get_id(*xml_id.split('.'))
-        return action_obj.get_action_values(self._name, action_id)
+        ModelData = pool.get('ir.model.data')
+        Action = pool.get('ir.action')
+        action_id = ModelData.get_id(*xml_id.split('.'))
+        return Action.get_action_values(cls.__name__, [action_id])[0]
 
-    def copy(self, ids, default=None):
-        action_obj = Pool().get('ir.action')
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+    @classmethod
+    def copy(cls, actions, default=None):
+        Action = Pool().get('ir.action')
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for act_window in self.browse(ids):
-            default['action'] = action_obj.copy(act_window.action.id)
-            new_id = super(ActionActWindow, self).copy(act_window.id,
-                default=default)
-            new_ids.append(new_id)
-
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-ActionActWindow()
+        new_actions = []
+        for act_window in actions:
+            default['action'] = Action.copy([act_window.action])[0].id
+            new_actions.extend(super(ActionActWindow, cls).copy([act_window],
+                    default=default))
+        return new_actions
 
 
 class ActionActWindowView(ModelSQL, ModelView):
     "Action act window view"
-    _name = 'ir.action.act_window.view'
+    __name__ = 'ir.action.act_window.view'
     _rec_name = 'view'
-    _description = __doc__
     sequence = fields.Integer('Sequence', required=True)
     view = fields.Many2One('ir.ui.view', 'View', required=True,
             ondelete='CASCADE')
     act_window = fields.Many2One('ir.action.act_window', 'Action',
             ondelete='CASCADE')
 
-    def __init__(self):
-        super(ActionActWindowView, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ActionActWindowView, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
-        super(ActionActWindowView, self).init(module_name)
-        table = TableHandler(Transaction().cursor, self, module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ActionActWindowView, cls).__register__(module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
 
         # Migration from 1.0 remove multi
         table.drop_column('multi')
 
-ActionActWindowView()
-
 
 class ActionWizard(ModelSQL, ModelView):
     "Action wizard"
-    _name = 'ir.action.wizard'
-    _description = __doc__
+    __name__ = 'ir.action.wizard'
     _inherits = {'ir.action': 'action'}
     wiz_name = fields.Char('Wizard name', required=True)
     action = fields.Many2One('ir.action', 'Action', required=True,
@@ -837,134 +790,115 @@
     email = fields.Char('Email')
     window = fields.Boolean('Window', help='Run wizard in a new window')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'ir.action.wizard'
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         later = {}
         vals = vals.copy()
         for field in vals:
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'set'):
+            if (field in cls._fields
+                    and hasattr(cls._fields[field], 'set')):
                 later[field] = vals[field]
         for field in later:
             del vals[field]
         cursor = Transaction().cursor
-        if cursor.nextid(self._table):
-            cursor.setnextid(self._table, cursor.currid('ir_action'))
-        new_id = super(ActionWizard, self).create(vals)
-        wizard = self.browse(new_id)
+        if cursor.nextid(cls._table):
+            cursor.setnextid(cls._table, cursor.currid('ir_action'))
+        wizard = super(ActionWizard, cls).create(vals)
         new_id = wizard.action.id
-        cursor.execute('UPDATE "' + self._table + '" SET id = %s ' \
+        cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \
                 'WHERE id = %s', (wizard.action.id, wizard.id))
-        cursor.update_auto_increment(self._table, wizard.action.id)
-        ModelStorage.delete(self, wizard.id)
-        self.write(new_id, later)
-        return new_id
+        cursor.update_auto_increment(cls._table, wizard.action.id)
+        wizard = cls(new_id)
+        cls.write([wizard], later)
+        return wizard
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, wizards):
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        action_ids = [x.action.id for x in self.browse(ids)]
+        actions = [x.action.id for x in wizards]
 
-        res = super(ActionWizard, self).delete(ids)
-        action_obj.delete(action_ids)
-        return res
+        super(ActionWizard, cls).delete(wizards)
+        Action.delete(actions)
 
-    def copy(self, ids, default=None):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def copy(cls, wizards, default=None):
+        Action = Pool().get('ir.action')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for wizard in self.browse(ids):
-            default['action'] = action_obj.copy(wizard.action.id)
-            new_id = super(ActionWizard, self).copy(wizard.id,
-                default=default)
-            new_ids.append(new_id)
-
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-ActionWizard()
+        new_wizards = []
+        for wizard in wizards:
+            default['action'] = Action.copy([wizard.action])[0].id
+            new_wizards.extend(super(ActionWizard, cls).copy([wizard],
+                    default=default))
+        return new_wizards
 
 
 class ActionURL(ModelSQL, ModelView):
     "Action URL"
-    _name = 'ir.action.url'
-    _description = __doc__
+    __name__ = 'ir.action.url'
     _inherits = {'ir.action': 'action'}
     url = fields.Char('Action Url', required=True)
     action = fields.Many2One('ir.action', 'Action', required=True,
             ondelete='CASCADE')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'ir.action.url'
 
-    def default_target(self):
+    @staticmethod
+    def default_target():
         return 'new'
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         later = {}
         vals = vals.copy()
         for field in vals:
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'set'):
+            if (field in cls._fields
+                    and hasattr(cls._fields[field], 'set')):
                 later[field] = vals[field]
         for field in later:
             del vals[field]
         cursor = Transaction().cursor
-        if cursor.nextid(self._table):
-            cursor.setnextid(self._table, cursor.currid('ir_action'))
-        new_id = super(ActionURL, self).create(vals)
-        url = self.browse(new_id)
+        if cursor.nextid(cls._table):
+            cursor.setnextid(cls._table, cursor.currid('ir_action'))
+        url = super(ActionURL, cls).create(vals)
         new_id = url.action.id
-        cursor.execute('UPDATE "' + self._table + '" SET id = %s ' \
+        cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \
                 'WHERE id = %s', (url.action.id, url.id))
-        cursor.update_auto_increment(self._table, url.action.id)
-        ModelStorage.delete(self, url.id)
-        self.write(new_id, later)
-        return new_id
+        cursor.update_auto_increment(cls._table, url.action.id)
+        url = cls(new_id)
+        cls.write([url], later)
+        return url
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, urls):
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        action_ids = [x.action.id for x in self.browse(ids)]
+        actions = [x.action for x in urls]
 
-        res = super(ActionURL, self).delete(ids)
-        action_obj.delete(action_ids)
-        return res
+        super(ActionURL, cls).delete(urls)
+        Action.delete(actions)
 
-    def copy(self, ids, default=None):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def copy(cls, urls, default=None):
+        Action = Pool().get('ir.action')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for url in self.browse(ids):
-            default['action'] = action_obj.copy(url.action.id)
-            new_id = super(ActionURL, self).copy(url.id, default=default)
-            new_ids.append(new_id)
-
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-ActionURL()
+        new_urls = []
+        for url in urls:
+            default['action'] = Action.copy([url.action])[0].id
+            new_urls.extend(super(ActionURL, cls).copy([url],
+                    default=default))
+        return new_urls
diff -r fb51f5b4aa4c trytond/ir/attachment.py
--- a/trytond/ir/attachment.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/attachment.py	Mon Sep 24 20:36:36 2012 +0200
@@ -13,6 +13,10 @@
 from ..pyson import Eval
 from ..pool import Pool
 
+__all__ = [
+    'Attachment',
+    ]
+
 
 def firstline(description):
     try:
@@ -23,8 +27,7 @@
 
 class Attachment(ModelSQL, ModelView):
     "Attachment"
-    _name = 'ir.attachment'
-    _description = __doc__
+    __name__ = 'ir.attachment'
     name = fields.Char('Name', required=True)
     type = fields.Selection([
         ('data', 'Data'),
@@ -35,7 +38,7 @@
                 }, depends=['type']), 'get_data', setter='set_data')
     description = fields.Text('Description')
     summary = fields.Function(fields.Char('Summary',
-        on_change_with=['description']), 'get_summary')
+        on_change_with=['description']), 'on_change_with_summary')
     resource = fields.Reference('Resource', selection='models_get',
         select=True)
     link = fields.Char('Link', states={
@@ -47,23 +50,25 @@
                 'invisible': Eval('type') != 'data',
                 }, depends=['type']), 'get_data')
     last_modification = fields.Function(fields.DateTime('Last Modification'),
-            'get_last_modification')
+        'get_last_modification')
     last_user = fields.Function(fields.Char('Last User'),
         'get_last_user')
 
-    def __init__(self):
-        super(Attachment, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Attachment, cls).__setup__()
+        cls._sql_constraints += [
             ('resource_name', 'UNIQUE(resource, name)',
                 'The  names of attachments must be unique by resource!'),
         ]
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
 
-        super(Attachment, self).init(module_name)
+        super(Attachment, cls).__register__(module_name)
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.4 res_model and res_id merged into resource
         # Reference
@@ -71,58 +76,61 @@
                 table.column_exist('res_id'):
             table.drop_constraint('res_model_res_id_name')
             cursor.execute('UPDATE "%s" '
-            'SET "resource" = "res_model"||\',\'||"res_id"' % self._table)
+            'SET "resource" = "res_model"||\',\'||"res_id"' % cls._table)
             table.drop_column('res_model')
             table.drop_column('res_id')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'data'
 
-    def default_resource(self):
+    @staticmethod
+    def default_resource():
         return Transaction().context.get('resource')
 
-    def default_collision(self):
+    @staticmethod
+    def default_collision():
         return 0
 
-    def models_get(self):
+    @staticmethod
+    def models_get():
         pool = Pool()
-        model_obj = pool.get('ir.model')
-        model_ids = model_obj.search([])
+        Model = pool.get('ir.model')
+        models = Model.search([])
         res = []
-        for model in model_obj.browse(model_ids):
+        for model in models:
             res.append([model.model, model.name])
         return res
 
-    def get_data(self, ids, name):
-        res = {}
+    def get_data(self, name):
         db_name = Transaction().cursor.dbname
-        format_ = Transaction().context.pop('%s.%s' % (self._name, name), '')
-        for attachment in self.browse(ids):
-            value = None
+        format_ = Transaction().context.pop('%s.%s'
+            % (self.__name__, name), '')
+        value = None
+        if name == 'data_size' or format_ == 'size':
+            value = 0
+        if self.digest:
+            filename = self.digest
+            if self.collision:
+                filename = filename + '-' + str(self.collision)
+            filename = os.path.join(CONFIG['data_path'], db_name,
+                    filename[0:2], filename[2:4], filename)
             if name == 'data_size' or format_ == 'size':
-                value = 0
-            if attachment.digest:
-                filename = attachment.digest
-                if attachment.collision:
-                    filename = filename + '-' + str(attachment.collision)
-                filename = os.path.join(CONFIG['data_path'], db_name,
-                        filename[0:2], filename[2:4], filename)
-                if name == 'data_size' or format_ == 'size':
-                    try:
-                        statinfo = os.stat(filename)
-                        value = statinfo.st_size
-                    except OSError:
-                        pass
-                else:
-                    try:
-                        with open(filename, 'rb') as file_p:
-                            value = buffer(file_p.read())
-                    except IOError:
-                        pass
-            res[attachment.id] = value
-        return res
+                try:
+                    statinfo = os.stat(filename)
+                    value = statinfo.st_size
+                except OSError:
+                    pass
+            else:
+                try:
+                    with open(filename, 'rb') as file_p:
+                        value = buffer(file_p.read())
+                except IOError:
+                    pass
+        return value
 
-    def set_data(self, ids, name, value):
+    @classmethod
+    def set_data(cls, attachments, name, value):
         if value is None:
             return
         cursor = Transaction().cursor
@@ -168,77 +176,73 @@
         else:
             with open(filename, 'wb') as file_p:
                 file_p.write(value)
-        self.write(ids, {
+        cls.write(attachments, {
             'digest': digest,
             'collision': collision,
             })
 
-    def get_summary(self, ids, name):
-        return dict((x.id, firstline(x.description or ''))
-            for x in self.browse(ids))
+    def on_change_with_summary(self, name=None):
+        return firstline(self.description or '')
 
-    def on_change_with_summary(self, values):
-        return firstline(values.get('description') or '')
+    def get_last_modification(self, name):
+        return (self.write_date if self.write_date else self.create_date
+            ).replace(microsecond=0)
 
-    def get_last_modification(self, ids, name):
-        return dict((x.id, (x.write_date if x.write_date else x.create_date
-            ).replace(microsecond=0))
-            for x in self.browse(ids))
+    @classmethod
+    def get_last_user(cls, attachments, name):
+        with Transaction().set_user(0):
+            return dict(
+                (x.id, x.write_uid.rec_name
+                    if x.write_uid else x.create_uid.rec_name)
+                for x in cls.browse(attachments))
 
-    def get_last_user(self, ids, name):
-        with Transaction().set_user(0):
-            return dict((x.id, x.write_uid.rec_name if x.write_uid
-                else x.create_uid.rec_name) for x in self.browse(ids))
-
-    def check_access(self, ids, mode='read'):
+    @classmethod
+    def check_access(cls, ids, mode='read'):
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
+        ModelAccess = pool.get('ir.model.access')
         if Transaction().user == 0:
             return
-        if not ids:
-            return
-        if isinstance(ids, (int, long)):
-            ids = [ids]
         model_names = set()
         with Transaction().set_user(0):
-            for attachment in self.browse(ids):
+            for attachment in cls.browse(ids):
                 if attachment.resource:
                     model_names.add(attachment.resource.split(',')[0])
         for model_name in model_names:
-            model_access_obj.check(model_name, mode=mode)
+            ModelAccess.check(model_name, mode=mode)
 
-    def read(self, ids, fields_names=None):
-        self.check_access(ids, mode='read')
-        return super(Attachment, self).read(ids, fields_names=fields_names)
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        cls.check_access(ids, mode='read')
+        return super(Attachment, cls).read(ids, fields_names=fields_names)
 
-    def delete(self, ids):
-        self.check_access(ids, mode='delete')
-        return super(Attachment, self).delete(ids)
+    @classmethod
+    def delete(cls, attachments):
+        cls.check_access([a.id for a in attachments], mode='delete')
+        super(Attachment, cls).delete(attachments)
 
-    def write(self, ids, vals):
-        self.check_access(ids, mode='write')
-        res = super(Attachment, self).write(ids, vals)
-        self.check_access(ids, mode='write')
-        return res
+    @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 create(self, vals):
-        res = super(Attachment, self).create(vals)
-        self.check_access(res, mode='create')
-        return res
+    @classmethod
+    def create(cls, vals):
+        attachment = super(Attachment, cls).create(vals)
+        cls.check_access([attachment.id], mode='create')
+        return attachment
 
-    def view_header_get(self, value, view_type='form'):
+    @classmethod
+    def view_header_get(cls, value, view_type='form'):
         pool = Pool()
-        ir_model_obj = pool.get('ir.model')
-        value = super(Attachment, self).view_header_get(value,
+        Model = pool.get('ir.model')
+        value = super(Attachment, cls).view_header_get(value,
                 view_type=view_type)
         resource = Transaction().context.get('resource')
         if resource:
             model_name, record_id = resource.split(',', 1)
-            ir_model_id, = ir_model_obj.search([('model', '=', model_name)])
-            ir_model = ir_model_obj.browse(ir_model_id)
-            model_obj = pool.get(model_name)
-            record = model_obj.browse(int(record_id))
+            ir_model, = Model.search([('model', '=', model_name)])
+            Resource = pool.get(model_name)
+            record = Resource(int(record_id))
             value = '%s - %s - %s' % (ir_model.name, record.rec_name, value)
         return value
-
-Attachment()
diff -r fb51f5b4aa4c trytond/ir/cache.py
--- a/trytond/ir/cache.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/cache.py	Mon Sep 24 20:36:36 2012 +0200
@@ -2,12 +2,13 @@
 #this repository contains the full copyright notices and license terms.
 from ..model import ModelSQL, fields
 
+__all__ = [
+    'Cache',
+    ]
+
 
 class Cache(ModelSQL):
     "Cache"
-    _name = 'ir.cache'
-    _description = __doc__
+    __name__ = 'ir.cache'
     name = fields.Char('Name', required=True)
     timestamp = fields.DateTime('Timestamp')
-
-Cache()
diff -r fb51f5b4aa4c trytond/ir/cron.py
--- a/trytond/ir/cron.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/cron.py	Mon Sep 24 20:36:36 2012 +0200
@@ -13,6 +13,10 @@
 from ..pool import Pool
 from ..backend import TableHandler
 
+__all__ = [
+    'Cron',
+    ]
+
 _INTERVALTYPES = {
     'days': lambda interval: relativedelta(days=interval),
     'hours': lambda interval: relativedelta(hours=interval),
@@ -24,8 +28,7 @@
 
 class Cron(ModelSQL, ModelView):
     "Cron"
-    _name = "ir.cron"
-    _description = __doc__
+    __name__ = "ir.cron"
     name = fields.Char('Name', required=True, translate=True)
     user = fields.Many2One('res.user', 'Execution User', required=True,
         domain=[('active', '=', False)],
@@ -53,65 +56,76 @@
     function = fields.Char('Function')
     args = fields.Text('Arguments')
 
-    def __init__(self):
-        super(Cron, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Cron, cls).__setup__()
+        cls._error_messages.update({
             'request_title': 'Scheduled action failed',
             'request_body': "The following action failed to execute "
                             "properly: \"%s\"\n Traceback: \n\n%s\n"
             })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
 
         # Migration from 2.0: rename numbercall, doall and nextcall
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('numbercall', 'number_calls')
         table.column_rename('doall', 'repeat_missed')
         table.column_rename('nextcall', 'next_call')
         table.drop_column('running')
 
-        super(Cron, self).init(module_name)
+        super(Cron, cls).__register__(module_name)
 
         # Migration from 2.0: work_days removed
         cursor.execute('UPDATE "%s" '
             'SET interval_type = %%s '
-            'WHERE interval_type = %%s' % self._table,
+            'WHERE interval_type = %%s' % cls._table,
             ('days', 'work_days'))
 
-    def default_next_call(self):
+    @staticmethod
+    def default_next_call():
         return datetime.datetime.now()
 
-    def default_interval_number(self):
+    @staticmethod
+    def default_interval_number():
         return 1
 
-    def default_interval_type(self):
+    @staticmethod
+    def default_interval_type():
         return 'months'
 
-    def default_number_calls(self):
+    @staticmethod
+    def default_number_calls():
         return -1
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_repeat_missed(self):
+    @staticmethod
+    def default_repeat_missed():
         return True
 
-    def check_xml_record(self, ids, values):
+    @staticmethod
+    def check_xml_record(crons, values):
         return True
 
-    def get_delta(self, cron):
+    @staticmethod
+    def get_delta(cron):
         '''
         Return the relativedelta for the next call
         '''
         return _INTERVALTYPES[cron.interval_type](cron.interval_number)
 
-    def _get_request_values(self, cron):
+    @classmethod
+    def _get_request_values(cls, cron):
         tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
         tb_s = tb_s.decode('utf-8', 'ignore')
-        name = self.raise_user_error('request_title',
+        name = cls.raise_user_error('request_title',
             raise_exception=False)
-        body = self.raise_user_error('request_body', (cron.name, tb_s),
+        body = cls.raise_user_error('request_body', (cron.name, tb_s),
             raise_exception=False)
         values = {
             'name': name,
@@ -122,7 +136,7 @@
             'date_sent': datetime.datetime.now(),
             'references': [
                 ('create', {
-                    'reference': '%s,%s' % (self._name, cron.id),
+                    'reference': '%s,%s' % (cls.__name__, cron.id),
                 }),
             ],
             'state': 'waiting',
@@ -130,35 +144,36 @@
         }
         return values
 
-    def _callback(self, cron):
+    @classmethod
+    def _callback(cls, cron):
         pool = Pool()
         try:
             args = (cron.args or []) and safe_eval(cron.args)
-            model_obj = pool.get(cron.model)
+            Model = pool.get(cron.model)
             with Transaction().set_user(cron.user.id):
-                getattr(model_obj, cron.function)(*args)
+                getattr(Model, cron.function)(*args)
         except Exception:
             Transaction().cursor.rollback()
 
-            request_obj = pool.get('res.request')
+            Request = pool.get('res.request')
             req_user = cron.request_user
             language = (req_user.language.code if req_user.language
                     else CONFIG['language'])
             with contextlib.nested(Transaction().set_user(cron.user.id),
                     Transaction().set_context(language=language)):
-                values = self._get_request_values(cron)
-                request_obj.create(values)
+                values = cls._get_request_values(cron)
+                Request.create(values)
             Transaction().cursor.commit()
 
-    def run(self, db_name):
+    @classmethod
+    def run(cls, db_name):
         now = datetime.datetime.now()
         with Transaction().start(db_name, 0) as transaction:
-            transaction.cursor.lock(self._table)
-            cron_ids = self.search([
-                ('number_calls', '!=', 0),
-                ('next_call', '<=', datetime.datetime.now()),
-            ])
-            crons = self.browse(cron_ids)
+            transaction.cursor.lock(cls._table)
+            crons = cls.search([
+                    ('number_calls', '!=', 0),
+                    ('next_call', '<=', datetime.datetime.now()),
+                    ])
 
             for cron in crons:
                 try:
@@ -167,19 +182,17 @@
                     first = True
                     while next_call < now and number_calls != 0:
                         if first or cron.repeat_missed:
-                            self._callback(cron)
-                        next_call += self.get_delta(cron)
+                            cls._callback(cron)
+                        next_call += cls.get_delta(cron)
                         if number_calls > 0:
                             number_calls -= 1
                         first = False
 
-                    values = {
-                        'next_call': next_call,
-                        'number_calls': number_calls,
-                    }
+                    cron.next_call = next_call
+                    cron.number_calls = number_calls
                     if not number_calls:
-                        values['active'] = False
-                    self.write(cron.id, values)
+                        cron.active = False
+                    cron.save()
                     transaction.cursor.commit()
                 except Exception:
                     transaction.cursor.rollback()
@@ -188,5 +201,3 @@
                     tb_s = tb_s.decode('utf-8', 'ignore')
                     logger = logging.getLogger('cron')
                     logger.error('Exception:\n%s' % tb_s)
-
-Cron()
diff -r fb51f5b4aa4c trytond/ir/date.py
--- a/trytond/ir/date.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/date.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,26 +1,29 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level
 #of this repository contains the full copyright notices and license terms.
+import datetime
+
 from ..model import Model
-import datetime
+from ..rpc import RPC
+
+__all__ = [
+    'Date',
+    ]
 
 
 class Date(Model):
     'Date'
-    _name = 'ir.date'
-    _description = __doc__
+    __name__ = 'ir.date'
 
-    def __init__(self):
-        super(Date, self).__init__()
-        self._rpc.update({
-            'today': False,
-            })
+    @classmethod
+    def __setup__(cls):
+        super(Date, cls).__setup__()
+        cls.__rpc__.update({
+                'today': RPC(),
+                })
 
-    def today(self):
+    @staticmethod
+    def today():
         '''
-        Current date
-
-        :return: a current datetime.date
+        Return the current date
         '''
         return datetime.date.today()
-
-Date()
diff -r fb51f5b4aa4c trytond/ir/export.py
--- a/trytond/ir/export.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/export.py	Mon Sep 24 20:36:36 2012 +0200
@@ -3,25 +3,23 @@
 "Exports"
 from ..model import ModelView, ModelSQL, fields
 
+__all__ = [
+    'Export', 'ExportLine',
+    ]
+
 
 class Export(ModelSQL, ModelView):
     "Export"
-    _name = "ir.export"
-    _description = __doc__
+    __name__ = "ir.export"
     name = fields.Char('Name')
     resource = fields.Char('Resource')
     export_fields = fields.One2Many('ir.export.line', 'export',
        'Fields')
 
-Export()
-
 
 class ExportLine(ModelSQL, ModelView):
     "Export line"
-    _name = 'ir.export.line'
-    _description = __doc__
+    __name__ = 'ir.export.line'
     name = fields.Char('Name')
     export = fields.Many2One('ir.export', 'Export',
        select=True)
-
-ExportLine()
diff -r fb51f5b4aa4c trytond/ir/lang.py
--- a/trytond/ir/lang.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/lang.py	Mon Sep 24 20:36:36 2012 +0200
@@ -4,7 +4,7 @@
 import warnings
 
 from ..model import ModelView, ModelSQL, fields
-from ..model.cacheable import Cacheable
+from ..cache import Cache
 from ..tools import safe_eval, datetime_strftime
 from ..transaction import Transaction
 from ..pool import Pool
@@ -15,11 +15,14 @@
 from locale import CHAR_MAX
 warnings.resetwarnings()
 
+__all__ = [
+    'Lang',
+    ]
 
-class Lang(ModelSQL, ModelView, Cacheable):
+
+class Lang(ModelSQL, ModelView):
     "Language"
-    _name = "ir.lang"
-    _description = __doc__
+    __name__ = "ir.lang"
     name = fields.Char('Name', required=True, translate=True)
     code = fields.Char('Code', required=True,
             help="RFC 4646 tag: http://tools.ietf.org/html/rfc4646")
@@ -38,75 +41,88 @@
     decimal_point = fields.Char('Decimal Separator', required=True)
     thousands_sep = fields.Char('Thousands Separator')
 
-    def __init__(self):
-        super(Lang, self).__init__()
-        self._constraints += [
+    _lang_cache = Cache('ir.lang')
+
+    @classmethod
+    def __setup__(cls):
+        super(Lang, cls).__setup__()
+        cls._constraints += [
             ('check_grouping', 'invalid_grouping'),
             ('check_date', 'invalid_date'),
         ]
-        self._sql_constraints += [
+        cls._sql_constraints += [
             ('check_decimal_point_thousands_sep',
                 'CHECK(decimal_point != thousands_sep)',
                 'decimal_point and thousands_sep must be different!'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_grouping': 'Invalid Grouping!',
             'invalid_date': 'The date format is not valid!',
         })
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([('code',) + clause[1:]], order=[])
-        if ids:
-            ids += self.search([('name',) + clause[1:]], order=[])
-            return [('id', 'in', ids)]
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        langs = cls.search([('code',) + clause[1:]], order=[])
+        if langs:
+            langs += cls.search([('name',) + clause[1:]], order=[])
+            return [('id', 'in', [l.id for l in langs])]
         return [('name',) + tuple(clause[1:])]
 
-    def read(self, ids, fields_names=None):
+    @classmethod
+    def read(cls, ids, fields_names=None):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        res = super(Lang, self).read(ids, fields_names=fields_names)
+        Translation = pool.get('ir.translation')
+        res = super(Lang, cls).read(ids, fields_names=fields_names)
         if (Transaction().context.get('translate_name')
                 and (not fields_names or 'name' in fields_names)):
             with Transaction().set_context(
                     language=CONFIG['language'],
                     translate_name=False):
-                res2 = self.read(ids, fields_names=['id', 'code', 'name'])
+                res2 = cls.read(ids, fields_names=['id', 'code', 'name'])
             for record2 in res2:
                 for record in res:
                     if record['id'] == record2['id']:
                         break
-                res_trans = translation_obj.get_ids(self._name + ',name',
+                res_trans = Translation.get_ids(cls.__name__ + ',name',
                         'model', record2['code'], [record2['id']])
                 record['name'] = res_trans.get(record2['id'], False) \
                         or record2['name']
         return res
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_translatable(self):
+    @staticmethod
+    def default_translatable():
         return False
 
-    def default_direction(self):
+    @staticmethod
+    def default_direction():
         return 'ltr'
 
-    def default_date(self):
+    @staticmethod
+    def default_date():
         return '%m/%d/%Y'
 
-    def default_grouping(self):
+    @staticmethod
+    def default_grouping():
         return '[]'
 
-    def default_decimal_point(self):
+    @staticmethod
+    def default_decimal_point():
         return '.'
 
-    def default_thousands_sep(self):
+    @staticmethod
+    def default_thousands_sep():
         return ','
 
-    def check_grouping(self, ids):
+    @classmethod
+    def check_grouping(cls, langs):
         '''
         Check if grouping is list of numbers
         '''
-        for lang in self.browse(ids):
+        for lang in langs:
             try:
                 grouping = safe_eval(lang.grouping)
                 for i in grouping:
@@ -116,11 +132,12 @@
                 return False
         return True
 
-    def check_date(self, ids):
+    @classmethod
+    def check_date(cls, langs):
         '''
         Check the date format
         '''
-        for lang in self.browse(ids):
+        for lang in langs:
             try:
                 datetime_strftime(datetime.datetime.now(),
                         lang.date.encode('utf-8'))
@@ -145,38 +162,41 @@
                 return False
         return True
 
-    def check_xml_record(self, ids, values):
+    @staticmethod
+    def check_xml_record(langs, values):
         return True
 
-    def get_translatable_languages(self):
-        res = self.get('translatable_languages')
+    @classmethod
+    def get_translatable_languages(cls):
+        res = cls._lang_cache.get('translatable_languages')
         if res is None:
-            lang_ids = self.search([
+            langs = cls.search([
                     ('translatable', '=', True),
                     ])
-            res = [x.code for x in self.browse(lang_ids)]
-            self.add('translatable_languages', res)
+            res = [x.code for x in langs]
+            cls._lang_cache.set('translatable_languages', res)
         return res
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         # Clear cache
-        if self.get('translatable_languages'):
-            self.invalidate('translatable_languages')
-        return super(Lang, self).create(vals)
+        cls._lang_cache.clear()
+        return super(Lang, cls).create(vals)
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, langs, vals):
         # Clear cache
-        if self.get('translatable_languages'):
-            self.invalidate('translatable_languages')
-        return super(Lang, self).write(ids, vals)
+        cls._lang_cache.clear()
+        super(Lang, cls).write(langs, vals)
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, langs):
         # Clear cache
-        if self.get('translatable_languages'):
-            self.invalidate('translatable_languages')
-        return super(Lang, self).delete(ids)
+        cls._lang_cache.clear()
+        super(Lang, cls).delete(langs)
 
-    def _group(self, lang, s, monetary=False):
+    @staticmethod
+    def _group(lang, s, monetary=None):
         # Code from _group in locale.py
 
         # Iterate over grouping intervals
@@ -194,11 +214,11 @@
                 last_interval = interval
 
         if monetary:
-            thousands_sep = monetary['mon_thousands_sep']
-            grouping = safe_eval(monetary['mon_grouping'])
+            thousands_sep = monetary.mon_thousands_sep
+            grouping = safe_eval(monetary.mon_grouping)
         else:
-            thousands_sep = lang['thousands_sep']
-            grouping = safe_eval(lang['grouping'])
+            thousands_sep = lang.thousands_sep
+            grouping = safe_eval(lang.grouping)
         if not grouping:
             return (s, 0)
         if s[-1] == ' ':
@@ -225,20 +245,11 @@
             len(thousands_sep) * (len(groups) - 1)
         )
 
-    def format(self, lang, percent, value, grouping=False, monetary=None,
+    @classmethod
+    def format(cls, lang, percent, value, grouping=False, monetary=None,
             *additional):
         '''
         Returns the lang-aware substitution of a %? specifier (percent).
-
-        :param lang: the BrowseRecord of the language
-        :param percent: the string with %? specifier
-        :param value: the value
-        :param grouping: a boolean to take grouping into account
-        :param monetary: a BrowseRecord of the currency or None
-        :param additional: for format strings which contain one or more
-            modifiers
-
-        :return: the formatted string
         '''
         # Code from format in locale.py
 
@@ -255,11 +266,11 @@
             return s[lpos:rpos + 1]
 
         if not lang:
-            lang = {
-                'decimal_point': self.default_decimal_point(),
-                'thousands_sep': self.default_thousands_sep(),
-                'grouping': self.default_grouping(),
-            }
+            lang = cls(
+                decimal_point=cls.default_decimal_point(),
+                thousands_sep=cls.default_thousands_sep(),
+                grouping=cls.default_grouping(),
+                )
 
         # this is only for one-percent-specifier strings
         # and this should be checked
@@ -275,41 +286,34 @@
             seps = 0
             parts = formatted.split('.')
             if grouping:
-                parts[0], seps = self._group(lang, parts[0], monetary=monetary)
+                parts[0], seps = cls._group(lang, parts[0], monetary=monetary)
             if monetary:
-                decimal_point = monetary['mon_decimal_point']
+                decimal_point = monetary.mon_decimal_point
             else:
-                decimal_point = lang['decimal_point']
+                decimal_point = lang.decimal_point
             formatted = decimal_point.join(parts)
             if seps:
                 formatted = _strip_padding(formatted, seps)
         elif percent[-1] in 'diu':
             if grouping:
-                formatted, seps = self._group(lang, formatted,
+                formatted, seps = cls._group(lang, formatted,
                     monetary=monetary)
             if seps:
                 formatted = _strip_padding(formatted, seps)
         return formatted
 
-    def currency(self, lang, val, currency, symbol=True, grouping=False):
+    @classmethod
+    def currency(cls, lang, val, currency, symbol=True, grouping=False):
         """
         Formats val according to the currency settings in lang.
-
-        :param lang: the BrowseRecord of the language
-        :param val: the value to format
-        :param currency: the BrowseRecord of the currency
-        :param symbol: a boolean to include currency symbol
-        :param grouping: a boolean to take grouping into account
-
-        :return: the formatted string
         """
         # Code from currency in locale.py
         if not lang:
-            lang = {
-                'decimal_point': self.default_decimal_point(),
-                'thousands_sep': self.default_thousands_sep(),
-                'grouping': self.default_grouping(),
-            }
+            lang = cls(
+                decimal_point=cls.default_decimal_point(),
+                thousands_sep=cls.default_thousands_sep(),
+                grouping=cls.default_grouping(),
+                )
 
         # check for illegal values
         digits = currency.digits
@@ -317,7 +321,7 @@
             raise ValueError("Currency formatting is not possible using "
                              "the 'C' locale.")
 
-        s = self.format(lang, '%%.%if' % digits, abs(val), grouping,
+        s = cls.format(lang, '%%.%if' % digits, abs(val), grouping,
                 monetary=currency)
         # '<' and '>' are markers if the sign must be inserted
         # between symbol and value
@@ -355,14 +359,10 @@
 
         return s.replace('<', '').replace('>', '')
 
-    def strftime(self, datetime, code, format):
+    @staticmethod
+    def strftime(datetime, code, format):
         '''
         Convert datetime to a string as specified by the format argument.
-
-        :param datetime: a datetime
-        :param code: locale code
-        :param format: a string
-        :return: a unicode string
         '''
         if code in TIME_LOCALE:
             for f, i in (('%a', 6), ('%A', 6), ('%b', 1), ('%B', 1)):
@@ -374,5 +374,3 @@
         else:
             format = format.encode('utf-8')
         return datetime_strftime(datetime, format).decode('utf-8')
-
-Lang()
diff -r fb51f5b4aa4c trytond/ir/model.py
--- a/trytond/ir/model.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/model.py	Mon Sep 24 20:36:36 2012 +0200
@@ -9,13 +9,19 @@
 from ..cache import Cache
 from ..pool import Pool
 from ..pyson import Bool, Eval
+from ..rpc import RPC
+
+__all__ = [
+    'Model', 'ModelField', 'ModelAccess', 'ModelFieldAccess', 'ModelButton',
+    'ModelData', 'PrintModelGraphStart', 'PrintModelGraph', 'ModelGraph',
+    ]
+
 IDENTIFIER = re.compile(r'^[a-zA-z_][a-zA-Z0-9_]*$')
 
 
 class Model(ModelSQL, ModelView):
     "Model"
-    _name = 'ir.model'
-    _description = __doc__
+    __name__ = 'ir.model'
     name = fields.Char('Model Description', translate=True, loading='lazy',
         states={
             'readonly': Bool(Eval('module')),
@@ -36,74 +42,74 @@
     fields = fields.One2Many('ir.model.field', 'model', 'Fields',
        required=True)
 
-    def __init__(self):
-        super(Model, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Model, cls).__setup__()
+        cls._sql_constraints += [
             ('model_uniq', 'UNIQUE(model)',
                 'The model must be unique!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_module', 'invalid_module'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_module': 'Module Name must be a python identifier!',
         })
-        self._order.insert(0, ('model', 'ASC'))
-        self._rpc.update({
-                'list_models': False,
+        cls._order.insert(0, ('model', 'ASC'))
+        cls.__rpc__.update({
+                'list_models': RPC(),
                 })
 
-    def check_module(self, ids):
+    @classmethod
+    def check_module(cls, models):
         '''
         Check module
         '''
-        for model in self.browse(ids):
+        for model in models:
             if model.module and not IDENTIFIER.match(model.module):
                 return False
         return True
 
-    def list_models(self):
+    @classmethod
+    def list_models(cls):
         'Return a list of all models names'
         with Transaction().set_user(0):
-            model_ids = self.search([], order=[
+            models = cls.search([], order=[
                     ('module', 'ASC'),  # Optimization assumption
                     ('model', 'ASC'),
                     ('id', 'ASC'),
                     ])
-            models = self.browse(model_ids)
             return [m.model for m in models]
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        res = super(Model, self).create(vals)
+        Property = pool.get('ir.property')
+        res = super(Model, cls).create(vals)
         # Restart the cache of models_get
-        property_obj.models_get.reset()
+        Property._models_get_cache.clear()
         return res
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, models, vals):
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        res = super(Model, self).write(ids, vals)
+        Property = pool.get('ir.property')
+        super(Model, cls).write(models, vals)
         # Restart the cache of models_get
-        property_obj.models_get.reset()
-        return res
+        Property._models_get_cache.clear()
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, models):
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        res = super(Model, self).delete(ids)
+        Property = pool.get('ir.property')
+        super(Model, cls).delete(models)
         # Restart the cache of models_get
-        property_obj.models_get.reset()
-        return res
-
-Model()
+        Property._models_get_cache.clear()
 
 
 class ModelField(ModelSQL, ModelView):
     "Model field"
-    _name = 'ir.model.field'
-    _description = __doc__
+    __name__ = 'ir.model.field'
     name = fields.Char('Name', required=True,
         states={
             'readonly': Bool(Eval('module')),
@@ -141,43 +147,48 @@
     module = fields.Char('Module',
        help="Module in which this field is defined")
 
-    def __init__(self):
-        super(ModelField, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModelField, cls).__setup__()
+        cls._sql_constraints += [
             ('name_model_uniq', 'UNIQUE(name, model)',
                 'The field name in model must be unique!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_name', 'invalid_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_name': 'Model Field Name must be a python identifier!',
         })
-        self._order.insert(0, ('name', 'ASC'))
+        cls._order.insert(0, ('name', 'ASC'))
 
-    def default_name(self):
+    @staticmethod
+    def default_name():
         return 'No Name'
 
-    def default_field_description(self):
+    @staticmethod
+    def default_field_description():
         return 'No description available'
 
-    def check_name(self, ids):
+    @classmethod
+    def check_name(cls, fields):
         '''
         Check name
         '''
-        for field in self.browse(ids):
+        for field in fields:
             if not IDENTIFIER.match(field.name):
                 return False
         return True
 
-    def read(self, ids, fields_names=None):
+    @classmethod
+    def read(cls, ids, fields_names=None):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
 
         to_delete = []
         if Transaction().context.get('language'):
             if fields_names is None:
-                fields_names = self._columns.keys()
+                fields_names = cls._fields.keys()
 
             if 'field_description' in fields_names \
                     or 'help' in fields_names:
@@ -188,11 +199,7 @@
                     fields_names.append('name')
                     to_delete.append('name')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-        res = super(ModelField, self).read(ids, fields_names=fields_names)
+        res = super(ModelField, cls).read(ids, fields_names=fields_names)
 
         if (Transaction().context.get('language')
                 and ('field_description' in fields_names
@@ -221,20 +228,20 @@
                 if 'help' in fields_names:
                     trans_args.append((id2model[model_id] + ',' + rec['name'],
                             'help', Transaction().language, None))
-            translation_obj.get_sources(trans_args)
+            Translation.get_sources(trans_args)
             for rec in res:
                 if isinstance(rec['model'], (list, tuple)):
                     model_id = rec['model'][0]
                 else:
                     model_id = rec['model']
                 if 'field_description' in fields_names:
-                    res_trans = translation_obj.get_source(
+                    res_trans = Translation.get_source(
                             id2model[model_id] + ',' + rec['name'],
                             'field', Transaction().language)
                     if res_trans:
                         rec['field_description'] = res_trans
                 if 'help' in fields_names:
-                    res_trans = translation_obj.get_source(
+                    res_trans = Translation.get_source(
                             id2model[model_id] + ',' + rec['name'],
                             'help', Transaction().language)
                     if res_trans:
@@ -244,17 +251,12 @@
             for rec in res:
                 for field in to_delete:
                     del rec[field]
-        if int_id:
-            res = res[0]
         return res
 
-ModelField()
-
 
 class ModelAccess(ModelSQL, ModelView):
     "Model access"
-    _name = 'ir.model.access'
-    _description = __doc__
+    __name__ = 'ir.model.access'
     _rec_name = 'model'
     model = fields.Many2One('ir.model', 'Model', required=True,
             ondelete="CASCADE")
@@ -265,44 +267,62 @@
     perm_create = fields.Boolean('Create Access')
     perm_delete = fields.Boolean('Delete Access')
     description = fields.Text('Description')
+    _get_access_cache = Cache('ir_model_access.get_access', context=False)
 
-    def __init__(self):
-        super(ModelAccess, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModelAccess, cls).__setup__()
+        cls._sql_constraints += [
             ('model_group_uniq', 'UNIQUE("model", "group")',
                 'Only one record by model and group is allowed!'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'read': 'You can not read this document! (%s)',
             'write': 'You can not write in this document! (%s)',
             'create': 'You can not create this kind of document! (%s)',
             'delete': 'You can not delete this document! (%s)',
             })
-        self._rpc.update({
-                'get_access': False,
+        cls.__rpc__.update({
+                'get_access': RPC(),
                 })
 
-    def check_xml_record(self, ids, values):
+    @staticmethod
+    def check_xml_record(accesses, values):
         return True
 
-    def default_perm_read(self):
+    @staticmethod
+    def default_perm_read():
         return False
 
-    def default_perm_write(self):
+    @staticmethod
+    def default_perm_write():
         return False
 
-    def default_perm_create(self):
+    @staticmethod
+    def default_perm_create():
         return False
 
-    def default_perm_delete(self):
+    @staticmethod
+    def default_perm_delete():
         return False
 
-    def get_access(self, models):
+    @classmethod
+    def get_access(cls, models):
         'Return access for models'
         pool = Pool()
-        ir_model_obj = pool.get('ir.model')
-        user_group_obj = pool.get('res.user-res.group')
+        Model = pool.get('ir.model')
+        UserGroup = pool.get('res.user-res.group')
         cursor = Transaction().cursor
+
+        access = {}
+        for model in models:
+            maccess = cls._get_access_cache.get(model, default=-1)
+            if maccess == -1:
+                break
+            access[model] = maccess
+        else:
+            return access
+
         default = {'read': True, 'write': True, 'create': True, 'delete': True}
         access = dict((m, default) for m in models)
         cursor.execute(('SELECT '
@@ -319,72 +339,57 @@
                 'WHERE m.model IN (' + ','.join(('%%s',) * len(models)) + ') '
                     'AND (gu."user" = %%s OR a."group" IS NULL) '
                 'GROUP BY m.model')
-            % (self._table, ir_model_obj._table, user_group_obj._table),
+            % (cls._table, Model._table, UserGroup._table),
             list(models) + [Transaction().user])
         access.update(dict(
                 (m, {'read': r, 'write': w, 'create': c, 'delete': d})
                 for m, r, w, c, d in cursor.fetchall()))
+        for model, maccess in access.iteritems():
+            cls._get_access_cache.set(model, maccess)
         return access
 
-    @Cache('ir_model_access.check')
-    def check(self, model_name, mode='read', raise_exception=True):
+    @classmethod
+    def check(cls, model_name, mode='read', raise_exception=True):
         'Check access for model_name and mode'
         assert mode in ['read', 'write', 'create', 'delete'], \
                 'Invalid access mode for security'
         if Transaction().user == 0:
             return True
 
-        access = self.get_access([model_name])[model_name][mode]
-        if not access:
+        access = cls.get_access([model_name])[model_name][mode]
+        if not access and access is not None:
             if raise_exception:
-                self.raise_user_error(mode, model_name)
+                cls.raise_user_error(mode, model_name)
             else:
                 return False
         return True
 
-    def write(self, ids, vals):
-        res = super(ModelAccess, self).write(ids, vals)
+    @classmethod
+    def write(cls, accesses, vals):
+        super(ModelAccess, cls).write(accesses, vals)
         # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        res = super(ModelAccess, cls).create(vals)
+        # Restart the cache
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         return res
 
-    def create(self, vals):
-        res = super(ModelAccess, self).create(vals)
+    @classmethod
+    def delete(cls, accesses):
+        super(ModelAccess, cls).delete(accesses)
         # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-    def delete(self, ids):
-        res = super(ModelAccess, self).delete(ids)
-        # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-ModelAccess()
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
 
 
 class ModelFieldAccess(ModelSQL, ModelView):
     "Model Field Access"
-    _name = 'ir.model.field.access'
-    _description = __doc__
+    __name__ = 'ir.model.field.access'
     _rec_name = 'field'
     field = fields.Many2One('ir.model.field', 'Field', required=True,
             ondelete='CASCADE')
@@ -394,46 +399,45 @@
     perm_create = fields.Boolean('Create Access')
     perm_delete = fields.Boolean('Delete Access')
     description = fields.Text('Description')
+    _get_access_cache = Cache('ir_model_field_access.check')
 
-    def __init__(self):
-        super(ModelFieldAccess, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModelFieldAccess, cls).__setup__()
+        cls._sql_constraints += [
             ('field_group_uniq', 'UNIQUE("field", "group")',
                 'Only one record by field and group is allowed!'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'read': 'You can not read the field! (%s.%s)',
             'write': 'You can not write on the field! (%s.%s)',
             })
 
-    def check_xml_record(self, ids, values):
+    @staticmethod
+    def check_xml_record(field_accesses, values):
         return True
 
-    def default_perm_read(self):
+    @staticmethod
+    def default_perm_read():
         return False
 
-    def default_perm_write(self):
+    @staticmethod
+    def default_perm_write():
         return False
 
-    def default_perm_create(self):
+    @staticmethod
+    def default_perm_create():
         return True
 
-    def default_perm_delete(self):
+    @staticmethod
+    def default_perm_delete():
         return True
 
-    @Cache('ir_model_field_access.check')
-    def check(self, model_name, fields, mode='read', raise_exception=True,
+    @classmethod
+    def check(cls, model_name, fields, mode='read', raise_exception=True,
             access=False):
         '''
         Check access for fields on model_name.
-
-        :param model_name: the model name
-        :param fields: a list of fields
-        :param mode: 'read', 'write', 'create' or 'delete'
-        :param raise_exception: raise an exception if the test failed
-        :param access: return a dictionary with access right instead of boolean
-
-        :return: a boolean
         '''
         assert mode in ('read', 'write', 'create', 'delete'), \
             'Invalid access mode'
@@ -443,134 +447,128 @@
             return True
 
         pool = Pool()
-        ir_model_obj = pool.get('ir.model')
-        ir_model_field_obj = pool.get('ir.model.field')
-        user_group_obj = pool.get('res.user-res.group')
+        Model = pool.get('ir.model')
+        ModelField = pool.get('ir.model.field')
+        UserGroup = pool.get('res.user-res.group')
 
         cursor = Transaction().cursor
+        key = (model_name, mode, access, Transaction().user)
+        accesses = cls._get_access_cache.get(key)
 
-        cursor.execute('SELECT f.name, '
-            'MAX(CASE WHEN a.perm_%s THEN 1 ELSE 0 END) '
-            'FROM "%s" AS a '
-            'JOIN "%s" AS f '
-                'ON (a.field = f.id) '
-            'JOIN "%s" AS m '
-                'ON (f.model = m.id) '
-            'LEFT JOIN "%s" AS gu '
-                'ON (gu."group" = a."group") '
-            'WHERE m.model = %%s AND (gu."user" = %%s OR a."group" IS NULL) '
-            'GROUP BY f.name'
-            % (mode, self._table, ir_model_field_obj._table,
-                ir_model_obj._table, user_group_obj._table), (model_name,
-                Transaction().user))
-        accesses = dict(cursor.fetchall())
+        if accesses is None:
+            cursor.execute('SELECT f.name, '
+                'MAX(CASE WHEN a.perm_%s THEN 1 ELSE 0 END) '
+                'FROM "%s" AS a '
+                'JOIN "%s" AS f '
+                    'ON (a.field = f.id) '
+                'JOIN "%s" AS m '
+                    'ON (f.model = m.id) '
+                'LEFT JOIN "%s" AS gu '
+                    'ON (gu."group" = a."group") '
+                'WHERE m.model = %%s '
+                    'AND (gu."user" = %%s OR a."group" IS NULL) '
+                'GROUP BY f.name'
+                % (mode, cls._table, ModelField._table,
+                    Model._table, UserGroup._table), (model_name,
+                    Transaction().user))
+            accesses = dict(cursor.fetchall())
+            cls._get_access_cache.set(key, accesses)
         if access:
             return accesses
         for field in fields:
             if not accesses.get(field, True):
                 if raise_exception:
-                    self.raise_user_error(mode, (model_name, field))
+                    cls.raise_user_error(mode, (model_name, field))
                 else:
                     return False
         return True
 
-    def write(self, ids, vals):
-        res = super(ModelFieldAccess, self).write(ids, vals)
+    @classmethod
+    def write(cls, field_accesses, vals):
+        super(ModelFieldAccess, cls).write(field_accesses, vals)
         # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        res = super(ModelFieldAccess, cls).create(vals)
+        # Restart the cache
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         return res
 
-    def create(self, vals):
-        res = super(ModelFieldAccess, self).create(vals)
+    @classmethod
+    def delete(cls, field_accesses):
+        super(ModelFieldAccess, cls).delete(field_accesses)
         # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-    def delete(self, ids):
-        res = super(ModelFieldAccess, self).delete(ids)
-        # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-ModelFieldAccess()
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
 
 
 class ModelButton(ModelSQL, ModelView):
     "Model Button"
-    _name = 'ir.model.button'
-    _description = __doc__
+    __name__ = 'ir.model.button'
     name = fields.Char('Name', required=True, readonly=True)
     model = fields.Many2One('ir.model', 'Model', required=True, readonly=True,
         ondelete='CASCADE', select=True)
     groups = fields.Many2Many('ir.model.button-res.group', 'button', 'group',
         'Groups')
+    _groups_cache = Cache('ir.model.button.groups')
 
-    def __init__(self):
-        super(ModelButton, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModelButton, cls).__setup__()
+        cls._sql_constraints += [
             ('name_model_uniq', 'UNIQUE(name, model)',
                 'The button name in model must be unique!'),
             ]
-        self._order.insert(0, ('model', 'ASC'))
+        cls._order.insert(0, ('model', 'ASC'))
 
-    def create(self, values):
-        result = super(ModelButton, self).create(values)
+    @classmethod
+    def create(cls, values):
+        result = super(ModelButton, cls).create(values)
         # Restart the cache for get_groups
-        self.get_groups.reset()
+        cls._groups_cache.clear()
         return result
 
-    def write(self, ids, values):
-        result = super(ModelButton, self).write(ids, values)
+    @classmethod
+    def write(cls, buttons, values):
+        super(ModelButton, cls).write(buttons, values)
         # Restart the cache for get_groups
-        self.get_groups.reset()
-        return result
+        cls._groups_cache.clear()
 
-    def delete(self, ids):
-        result = super(ModelButton, self).delete(ids)
+    @classmethod
+    def delete(cls, buttons):
+        super(ModelButton, cls).delete(buttons)
         # Restart the cache for get_groups
-        self.get_groups.reset()
-        return result
+        cls._groups_cache.clear()
 
-    @Cache('ir.model.button')
-    def get_groups(self, model, name):
+    @classmethod
+    def get_groups(cls, model, name):
         '''
         Return a set of group ids for the named button on the model.
         '''
-        button_ids = self.search([
+        key = (model, name)
+        groups = cls._groups_cache.get(key)
+        if groups is not None:
+            return groups
+        buttons = cls.search([
                 ('model.model', '=', model),
                 ('name', '=', name),
                 ])
-        if not button_ids:
-            return set()
-        button_id, = button_ids
-        button = self.browse(button_id)
-        return set(g.id for g in button.groups)
-
-ModelButton()
+        if not buttons:
+            groups = set()
+        else:
+            button, = buttons
+            groups = set(g.id for g in button.groups)
+        cls._groups_cache.set(key, groups)
+        return groups
 
 
 class ModelData(ModelSQL, ModelView):
     "Model data"
-    _name = 'ir.model.data'
-    _description = __doc__
+    __name__ = 'ir.model.data'
     fs_id = fields.Char('Identifier on File System', required=True,
         help="The id of the record as known on the file system.",
         select=True)
@@ -584,68 +582,70 @@
     values = fields.Text('Values')
     inherit = fields.Boolean('Inherit')
     noupdate = fields.Boolean('No Update')
+    _get_id_cache = Cache('ir_model_data.get_id', context=False)
 
-    def __init__(self):
-        super(ModelData, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(ModelData, cls).__setup__()
+        cls._sql_constraints = [
             ('fs_id_module_model_uniq', 'UNIQUE("fs_id", "module", "model")',
                 'The triple (fs_id, module, model) must be unique!'),
         ]
 
-    def default_date_init(self):
+    @staticmethod
+    def default_date_init():
         return datetime.datetime.now()
 
-    def default_inherit(self):
+    @staticmethod
+    def default_inherit():
         return False
 
-    def default_noupdate(self):
+    @staticmethod
+    def default_noupdate():
         return False
 
-    def write(self, ids, values):
-        result = super(ModelData, self).write(ids, values)
+    @classmethod
+    def write(cls, data, values):
+        super(ModelData, cls).write(data, values)
         # Restart the cache for get_id
-        self.get_id.reset()
-        return result
+        cls._get_id_cache.clear()
 
-    @Cache('ir_model_data.get_id')
-    def get_id(self, module, fs_id):
+    @classmethod
+    def get_id(cls, module, fs_id):
         """
         Return for an fs_id the corresponding db_id.
-
-        :param module: the module name
-        :param fs_id: the id in the xml file
-
-        :return: the database id
         """
-        ids = self.search([
+        key = (module, fs_id)
+        id_ = cls._get_id_cache.get(key)
+        if id_ is not None:
+            return id_
+        data = cls.search([
             ('module', '=', module),
             ('fs_id', '=', fs_id),
             ('inherit', '=', False),
             ], limit=1)
-        if not ids:
+        if not data:
             raise Exception("Reference to %s not found"
                 % ".".join([module, fs_id]))
-        return self.read(ids[0], ['db_id'])['db_id']
-
-ModelData()
+        id_ = cls.read([d.id for d in data], ['db_id'])[0]['db_id']
+        cls._get_id_cache.set(key, id_)
+        return id_
 
 
 class PrintModelGraphStart(ModelView):
     'Print Model Graph'
-    _name = 'ir.model.print_model_graph.start'
-    _description = __doc__
+    __name__ = 'ir.model.print_model_graph.start'
     level = fields.Integer('Level', required=True)
     filter = fields.Text('Filter', help="Entering a Python "
             "Regular Expression will exclude matching models from the graph.")
 
-    def default_level(self):
+    @staticmethod
+    def default_level():
         return 1
 
-PrintModelGraphStart()
-
 
 class PrintModelGraph(Wizard):
-    _name = 'ir.model.print_model_graph'
+    __name__ = 'ir.model.print_model_graph'
 
     start = StateView('ir.model.print_model_graph.start',
         'ir.print_model_graph_start_view_form', [
@@ -654,62 +654,56 @@
             ])
     print_ = StateAction('ir.report_model_graph')
 
-    def transition_print_(self, session):
+    def transition_print_(self):
         return 'end'
 
-    def do_print_(self, session, action):
+    def do_print_(self, action):
         return action, {
             'id': Transaction().context.get('active_id'),
             'ids': Transaction().context.get('active_ids'),
-            'level': session.start.level,
-            'filter': session.start.filter,
+            'level': self.start.level,
+            'filter': self.start.filter,
             }
 
-PrintModelGraph()
-
 
 class ModelGraph(Report):
-    _name = 'ir.model.graph'
+    __name__ = 'ir.model.graph'
 
-    def execute(self, ids, data):
+    @classmethod
+    def execute(cls, ids, data):
         import pydot
         pool = Pool()
-        model_obj = pool.get('ir.model')
-        action_report_obj = pool.get('ir.action.report')
+        Model = pool.get('ir.model')
+        ActionReport = pool.get('ir.action.report')
 
         if not data['filter']:
             filter = None
         else:
             filter = re.compile(data['filter'], re.VERBOSE)
-        action_report_ids = action_report_obj.search([
-            ('report_name', '=', self._name)
+        action_report_ids = ActionReport.search([
+            ('report_name', '=', cls.__name__)
             ])
         if not action_report_ids:
-            raise Exception('Error', 'Report (%s) not find!' % self._name)
-        action_report = action_report_obj.browse(action_report_ids[0])
+            raise Exception('Error', 'Report (%s) not find!' % cls.__name__)
+        action_report = ActionReport.browse(action_report_ids[0])
 
-        models = model_obj.browse(ids)
+        models = Model.browse(ids)
 
         graph = pydot.Dot(fontsize="8")
         graph.set('center', '1')
         graph.set('ratio', 'auto')
-        self.fill_graph(models, graph, level=data['level'], filter=filter)
+        cls.fill_graph(models, graph, level=data['level'], filter=filter)
         data = graph.create(prog='dot', format='png')
         return ('png', buffer(data), False, action_report.name)
 
-    def fill_graph(self, models, graph, level=1, filter=None):
+    @classmethod
+    def fill_graph(cls, models, graph, level=1, filter=None):
         '''
         Fills a pydot graph with a models structure.
-
-        :param models: a BrowseRecordList of ir.model
-        :param graph: a pydot.Graph
-        :param level: the depth to dive into model reationships
-        :param filter: a compiled regular expression object to filter specific
-            models
         '''
         import pydot
         pool = Pool()
-        model_obj = pool.get('ir.model')
+        Model = pool.get('ir.model')
 
         sub_models = set()
         if level > 0:
@@ -720,12 +714,12 @@
                     if field.relation and not graph.get_node(field.relation):
                         sub_models.add(field.relation)
             if sub_models:
-                model_ids = model_obj.search([
+                model_ids = Model.search([
                     ('model', 'in', list(sub_models)),
                     ])
-                sub_models = model_obj.browse(model_ids)
+                sub_models = Model.browse(model_ids)
                 if set(sub_models) != set(models):
-                    self.fill_graph(sub_models, graph, level=level - 1,
+                    cls.fill_graph(sub_models, graph, level=level - 1,
                             filter=filter)
 
         for model in models:
@@ -792,5 +786,3 @@
 
                     edge = pydot.Edge(str(tail), str(head), **args)
                     graph.add_edge(edge)
-
-ModelGraph()
diff -r fb51f5b4aa4c trytond/ir/module/module.py
--- a/trytond/ir/module/module.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/module/module.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,5 +1,10 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+try:
+    import simplejson as json
+except ImportError:
+    import json
+
 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, \
@@ -8,12 +13,20 @@
 from trytond.pool import Pool
 from trytond.transaction import Transaction
 from trytond.pyson import Eval
+from trytond.protocols.jsonrpc import object_hook
+from trytond.rpc import RPC
+
+__all__ = [
+    'Module', 'ModuleDependency', 'ModuleConfigWizardItem',
+    'ModuleConfigWizardFirst', 'ModuleConfigWizardOther', 'ModuleConfigWizard',
+    'ModuleInstallUpgradeStart', 'ModuleInstallUpgradeDone',
+    'ModuleInstallUpgrade', 'ModuleConfig',
+    ]
 
 
 class Module(ModelSQL, ModelView):
     "Module"
-    _name = "ir.module.module"
-    _description = __doc__
+    __name__ = "ir.module.module"
     name = fields.Char("Name", readonly=True, required=True)
     version = fields.Function(fields.Char('Version'), 'get_version')
     dependencies = fields.One2Many('ir.module.module.dependency',
@@ -30,24 +43,25 @@
         ('to install', 'To be installed'),
         ], string='State', readonly=True)
 
-    def __init__(self):
-        super(Module, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Module, cls).__setup__()
+        cls._sql_constraints = [
             ('name_uniq', 'unique (name)',
                 'The name of the module must be unique!'),
         ]
-        self._order.insert(0, ('name', 'ASC'))
-        self._rpc.update({
-            'on_write': False,
-        })
-        self._error_messages.update({
+        cls._order.insert(0, ('name', 'ASC'))
+        cls.__rpc__.update({
+                'on_write': RPC(instantiate=0),
+                })
+        cls._error_messages.update({
             'delete_state': 'You can not remove a module that is installed ' \
                     'or will be installed',
             'missing_dep': 'Missing dependencies %s for module "%s"',
             'uninstall_dep': 'The modules you are trying to uninstall ' \
                     'depends on installed modules:',
             })
-        self._buttons.update({
+        cls._buttons.update({
                 'install': {
                     'invisible': Eval('state') != 'uninstalled',
                     },
@@ -68,62 +82,54 @@
                     },
                 })
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'uninstalled'
 
-    def get_version(self, ids, name):
-        res = {}
-        for module in self.browse(ids):
-            res[module.id] = get_module_info(module.name).get('version', '')
-        return res
+    def get_version(self, name):
+        return get_module_info(self.name).get('version', '')
 
-    def get_parents(self, ids, name):
-        parents = {}
-        modules = self.browse(ids)
+    @classmethod
+    def get_parents(cls, modules, name):
         parent_names = list(set(d.name for m in modules
                     for d in m.dependencies))
-        parent_ids = self.search([
+        parents = cls.search([
                 ('name', 'in', parent_names),
                 ])
-        name2id = dict((m.name, m.id) for m in self.browse(parent_ids))
-        for module in self.browse(ids):
-            parents[module.id] = [name2id[d.name] for d in module.dependencies]
-        return parents
+        name2id = dict((m.name, m.id) for m in parents)
+        return dict((m.id, [name2id[d.name] for d in m.dependencies])
+            for m in modules)
 
-    def get_childs(self, ids, name):
-        childs = dict((i, []) for i in ids)
-        modules = self.browse(ids)
+    @classmethod
+    def get_childs(cls, modules, name):
+        child_ids = dict((m.id, []) for m in modules)
         names = [m.name for m in modules]
-        child_ids = self.search([
+        childs = cls.search([
                 ('dependencies.name', 'in', names),
                 ])
-        for child in self.browse(child_ids):
+        for child in childs:
             for dep in child.dependencies:
-                if dep.module.id in childs:
-                    childs[dep.module.id].append(child.id)
-        return childs
+                if dep.module.id in child_ids:
+                    child_ids[dep.module.id].append(child.id)
+        return child_ids
 
-    def delete(self, ids):
-        if not ids:
-            return True
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for module in self.browse(ids):
+    @classmethod
+    def delete(cls, records):
+        for module in records:
             if module.state in (
                     'installed',
                     'to upgrade',
                     'to remove',
                     'to install',
                     ):
-                self.raise_user_error('delete_state')
-        return super(Module, self).delete(ids)
+                cls.raise_user_error('delete_state')
+        return super(Module, cls).delete(records)
 
-    def on_write(self, ids):
-        if not ids:
-            return []
-        res = []
+    @classmethod
+    def on_write(cls, modules):
+        ids = set()
         graph, packages, later = create_graph(get_module_list())
-        for module in self.browse(ids):
+        for module in modules:
             if module.name not in graph:
                 continue
 
@@ -140,20 +146,22 @@
                     childs.update(get_childs(c))
                 return childs
             dependencies.update(get_childs(module))
-            res += self.search([
-                ('name', 'in', list(dependencies)),
-                ])
-        return list({}.fromkeys(res))
+            ids |= set(x.id for x in cls.search([
+                        ('name', 'in', list(dependencies)),
+                        ]))
+        return list(ids)
 
-    def state_install(self, ids):
+    @classmethod
+    @ModelView.button
+    def install(cls, modules):
         graph, packages, later = create_graph(get_module_list())
-        for module in self.browse(ids):
+        for module in modules:
             if module.name not in graph:
                 missings = []
                 for package, deps, xdep, info in packages:
                     if package == module.name:
                         missings = [x for x in deps if x not in graph]
-                self.raise_user_error('missing_dep', (missings, module.name))
+                cls.raise_user_error('missing_dep', (missings, module.name))
 
             def get_parents(module):
                 parents = set(p.name for p in module.parents)
@@ -161,23 +169,25 @@
                     parents.update(get_parents(p))
                 return parents
             dependencies = list(get_parents(module))
-            module_install_ids = self.search([
-                ('name', 'in', dependencies),
-                ('state', '=', 'uninstalled'),
-                ])
-            self.write(module_install_ids + [module.id], {
-                'state': 'to install',
-                })
+            modules_install = cls.search([
+                    ('name', 'in', dependencies),
+                    ('state', '=', 'uninstalled'),
+                    ])
+            cls.write(modules_install + [module], {
+                    'state': 'to install',
+                    })
 
-    def state_upgrade(self, ids):
+    @classmethod
+    @ModelView.button
+    def upgrade(cls, modules):
         graph, packages, later = create_graph(get_module_list())
-        for module in self.browse(ids):
+        for module in modules:
             if module.name not in graph:
                 missings = []
                 for package, deps, xdep, info in packages:
                     if package == module.name:
                         missings = [x for x in deps if x not in graph]
-                self.raise_user_error('missing_dep', (missings, module.name))
+                cls.raise_user_error('missing_dep', (missings, module.name))
 
             def get_childs(name, graph):
                 childs = set(x.name for x in graph[name].childs)
@@ -187,29 +197,26 @@
                 childs.update(childs2)
                 return childs
             dependencies = list(get_childs(module.name, graph))
-            module_installed_ids = self.search([
-                ('name', 'in', dependencies),
-                ('state', '=', 'installed'),
-                ])
-            self.write(module_installed_ids + [module.id], {
-                'state': 'to upgrade',
+            modules_installed = cls.search([
+                    ('name', 'in', dependencies),
+                    ('state', '=', 'installed'),
+                    ])
+            cls.write(modules_installed + [module], {
+                    'state': 'to upgrade',
+                    })
+
+    @classmethod
+    @ModelView.button
+    def install_cancel(cls, modules):
+        cls.write(modules, {
+                'state': 'uninstalled',
                 })
 
+    @classmethod
     @ModelView.button
-    def install(self, ids):
-        return self.state_install(ids)
-
-    @ModelView.button
-    def install_cancel(self, ids):
-        self.write(ids, {
-            'state': 'uninstalled',
-            })
-        return True
-
-    @ModelView.button
-    def uninstall(self, ids):
+    def uninstall(cls, modules):
         cursor = Transaction().cursor
-        for module in self.browse(ids):
+        for module in modules:
             cursor.execute('SELECT m.state, m.name ' \
                     'FROM ir_module_module_dependency d ' \
                     'JOIN ir_module_module m on (d.module = m.id) ' \
@@ -219,33 +226,28 @@
                             (module.name,))
             res = cursor.fetchall()
             if res:
-                self.raise_user_error('uninstall_dep',
+                cls.raise_user_error('uninstall_dep',
                         error_description='\n'.join(
                             '\t%s: %s' % (x[0], x[1]) for x in res))
-        self.write(ids, {'state': 'to remove'})
-        return True
+        cls.write(modules, {'state': 'to remove'})
 
+    @classmethod
     @ModelView.button
-    def uninstall_cancel(self, ids):
-        self.write(ids, {'state': 'installed'})
-        return True
+    def uninstall_cancel(cls, modules):
+        cls.write(modules, {'state': 'installed'})
 
+    @classmethod
     @ModelView.button
-    def upgrade(self, ids):
-        return self.state_upgrade(ids)
+    def upgrade_cancel(cls, modules):
+        cls.write(modules, {'state': 'installed'})
 
-    @ModelView.button
-    def upgrade_cancel(self, ids):
-        self.write(ids, {'state': 'installed'})
-        return True
-
-    # update the list of available packages
-    def update_list(self):
+    @classmethod
+    def update_list(cls):
+        'Update the list of available packages'
         count = 0
         module_names = get_module_list()
 
-        module_ids = self.search([])
-        modules = self.browse(module_ids)
+        modules = cls.search([])
         name2module = dict((m.name, m) for m in modules)
 
         # iterate through installed modules and mark them as being so
@@ -253,45 +255,42 @@
             if name in name2module:
                 module = name2module[name]
                 tryton = get_module_info(name)
-                self._update_dependencies(module, tryton.get('depends', []))
+                cls._update_dependencies(module, tryton.get('depends', []))
                 continue
 
             tryton = get_module_info(name)
             if not tryton:
                 continue
-            module_id = self.create({
+            module = cls.create({
                     'name': name,
                     'state': 'uninstalled',
                     })
             count += 1
-            module = self.browse(module_id)
-            self._update_dependencies(module, tryton.get('depends', []))
+            cls._update_dependencies(module, tryton.get('depends', []))
         return count
 
-    def _update_dependencies(self, module, depends=None):
+    @classmethod
+    def _update_dependencies(cls, module, depends=None):
         pool = Pool()
-        dependency_obj = pool.get('ir.module.module.dependency')
-        dependency_obj.delete([x.id for x in module.dependencies
-                if x.name not in depends])
+        Dependency = pool.get('ir.module.module.dependency')
+        Dependency.delete([x for x in module.dependencies
+            if x.name not in depends])
         if depends is None:
             depends = []
         # Restart Browse Cache for deleted dependencies
-        module = self.browse(module.id)
+        module = cls(module.id)
         dependency_names = [x.name for x in module.dependencies]
         for depend in depends:
             if depend not in dependency_names:
-                dependency_obj.create({
+                Dependency.create({
                         'module': module.id,
                         'name': depend,
                         })
 
-Module()
-
 
 class ModuleDependency(ModelSQL, ModelView):
     "Module dependency"
-    _name = "ir.module.module.dependency"
-    _description = __doc__
+    __name__ = "ir.module.module.dependency"
     name = fields.Char('Name')
     module = fields.Many2One('ir.module.module', 'Module', select=True,
        ondelete='CASCADE', required=True)
@@ -304,34 +303,29 @@
                 ('unknown', 'Unknown'),
                 ], 'State', readonly=True), 'get_state')
 
-    def __init__(self):
-        super(ModuleDependency, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModuleDependency, cls).__setup__()
+        cls._sql_constraints += [
             ('name_module_uniq', 'UNIQUE(name, module)',
                 'Dependency must be unique by module!'),
         ]
 
-    def get_state(self, ids, name):
-        result = {}
+    def get_state(self, name):
         pool = Pool()
-        module_obj = pool.get('ir.module.module')
-        for dependency in self.browse(ids):
-            ids = module_obj.search([
-                ('name', '=', dependency.name),
+        Module = pool.get('ir.module.module')
+        dependencies = Module.search([
+                ('name', '=', self.name),
                 ])
-            if ids:
-                result[dependency.id] = module_obj.browse(ids[0]).state
-            else:
-                result[dependency.id] = 'unknown'
-        return result
-
-ModuleDependency()
+        if dependencies:
+            return dependencies[0].state
+        else:
+            return 'unknown'
 
 
 class ModuleConfigWizardItem(ModelSQL, ModelView):
     "Config wizard to run after installing module"
-    _name = 'ir.module.module.config_wizard.item'
-    _description = __doc__
+    __name__ = 'ir.module.module.config_wizard.item'
     _rec_name = 'action'
     action = fields.Many2One('ir.action', 'Action', required=True,
         readonly=True)
@@ -341,57 +335,54 @@
         ('done', 'Done'),
         ], string='State', required=True, select=True)
 
-    def __init__(self):
-        super(ModuleConfigWizardItem, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ModuleConfigWizardItem, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
-        table = TableHandler(Transaction().cursor, self, module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        table = TableHandler(Transaction().cursor, cls, module_name)
 
         # Migrate from 2.2 remove name
         table.drop_column('name')
 
-        super(ModuleConfigWizardItem, self).init(module_name)
+        super(ModuleConfigWizardItem, cls).__register__(module_name)
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'open'
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return 10
 
-ModuleConfigWizardItem()
-
 
 class ModuleConfigWizardFirst(ModelView):
     'Module Config Wizard First'
-    _name = 'ir.module.module.config_wizard.first'
-    _description = __doc__
-
-ModuleConfigWizardFirst()
+    __name__ = 'ir.module.module.config_wizard.first'
 
 
 class ModuleConfigWizardOther(ModelView):
     'Module Config Wizard Other'
-    _name = 'ir.module.module.config_wizard.other'
-    _description = __doc__
+    __name__ = 'ir.module.module.config_wizard.other'
 
     percentage = fields.Float('Percentage', readonly=True)
 
-    def default_percentage(self):
+    @staticmethod
+    def default_percentage():
         pool = Pool()
-        item_obj = pool.get('ir.module.module.config_wizard.item')
-        done = item_obj.search([
+        Item = pool.get('ir.module.module.config_wizard.item')
+        done = Item.search([
             ('state', '=', 'done'),
             ], count=True)
-        all = item_obj.search([], count=True)
+        all = Item.search([], count=True)
         return 100.0 * done / all
 
-ModuleConfigWizardOther()
-
 
 class ModuleConfigWizard(Wizard):
     'Run config wizards'
-    _name = 'ir.module.module.config_wizard'
+    __name__ = 'ir.module.module.config_wizard'
 
     class ConfigStateAction(StateAction):
 
@@ -400,18 +391,18 @@
 
         def get_action(self):
             pool = Pool()
-            item_obj = pool.get('ir.module.module.config_wizard.item')
-            action_obj = pool.get('ir.action')
-            item_ids = item_obj.search([
+            Item = pool.get('ir.module.module.config_wizard.item')
+            Action = pool.get('ir.action')
+            items = Item.search([
                 ('state', '=', 'open'),
                 ], limit=1)
-            if item_ids:
-                item = item_obj.browse(item_ids[0])
-                item_obj.write(item.id, {
-                    'state': 'done',
-                    })
-                return action_obj.get_action_values(item.action.type,
-                    item.action.id)
+            if items:
+                item = items[0]
+                Item.write([item], {
+                        'state': 'done',
+                        })
+                return Action.get_action_values(item.action.type,
+                    [item.action.id])[0]
 
     start = StateTransition()
     first = StateView('ir.module.module.config_wizard.first',
@@ -426,45 +417,37 @@
             ])
     action = ConfigStateAction()
 
-    def transition_start(self, session):
-        res = self.transition_action(session)
+    def transition_start(self):
+        res = self.transition_action()
         if res == 'other':
             return 'first'
         return res
 
-    def transition_action(self, session):
+    def transition_action(self):
         pool = Pool()
-        item_obj = pool.get('ir.module.module.config_wizard.item')
-        item_ids = item_obj.search([
-            ('state', '=', 'open'),
-            ])
-        if item_ids:
+        Item = pool.get('ir.module.module.config_wizard.item')
+        items = Item.search([
+                ('state', '=', 'open'),
+                ])
+        if items:
             return 'other'
         return 'end'
 
-ModuleConfigWizard()
-
 
 class ModuleInstallUpgradeStart(ModelView):
     'Module Install Upgrade Start'
-    _name = 'ir.module.module.install_upgrade.start'
-    _description = __doc__
+    __name__ = 'ir.module.module.install_upgrade.start'
     module_info = fields.Text('Modules to update', readonly=True)
 
-ModuleInstallUpgradeStart()
-
 
 class ModuleInstallUpgradeDone(ModelView):
     'Module Install Upgrade Done'
-    _name = 'ir.module.module.install_upgrade.done'
-    _description = __doc__
-
-ModuleInstallUpgradeDone()
+    __name__ = 'ir.module.module.install_upgrade.done'
 
 
 class ModuleInstallUpgrade(Wizard):
     "Install / Upgrade modules"
-    _name = 'ir.module.module.install_upgrade'
+    __name__ = 'ir.module.module.install_upgrade'
 
     start = StateView('ir.module.module.install_upgrade.start',
         'ir.module_install_upgrade_start_view_form', [
@@ -478,49 +461,52 @@
             ])
     config = StateAction('ir.act_module_config_wizard')
 
-    def default_start(self, session, fields):
+    @staticmethod
+    def default_start(fields):
         pool = Pool()
-        module_obj = pool.get('ir.module.module')
-        module_ids = module_obj.search([
-            ('state', 'in', ['to upgrade', 'to remove', 'to install']),
-            ])
-        modules = module_obj.browse(module_ids)
+        Module = pool.get('ir.module.module')
+        modules = Module.search([
+                ('state', 'in', ['to upgrade', 'to remove', 'to install']),
+                ])
         return {
             'module_info': '\n'.join(x.name + ': ' + x.state \
-                    for x in modules),
-        }
+                for x in modules),
+            }
 
-    def transition_upgrade(self, session):
+    def transition_upgrade(self):
         pool = Pool()
-        module_obj = pool.get('ir.module.module')
-        lang_obj = pool.get('ir.lang')
+        Module = pool.get('ir.module.module')
+        Lang = pool.get('ir.lang')
+        Session = pool.get('ir.session.wizard')
         with Transaction().new_cursor() as transaction:
-            module_ids = module_obj.search([
+            modules = Module.search([
                 ('state', 'in', ['to upgrade', 'to remove', 'to install']),
                 ])
-            lang_ids = lang_obj.search([
+            langs = Lang.search([
                 ('translatable', '=', True),
                 ])
-            lang = [x.code for x in lang_obj.browse(lang_ids)]
+            lang = [x.code for x in langs]
             transaction.cursor.commit()
-        if module_ids:
+        if modules:
             pool.init(update=True, lang=lang)
-        if session:
-            # Don't store session to prevent concurrent update
-            for state_name in session.data:
-                getattr(session, state_name).dirty = False
+        # Don't store session to prevent concurrent update
+        session = Session(self._session_id)
+        data = json.loads(session.data.encode('utf-8'),
+            object_hook=object_hook)
+        for state_name, state in self.states.iteritems():
+            if isinstance(state, StateView):
+                Target = pool.get(state.model_name)
+                data.setdefault(state_name, {})
+                setattr(self, state_name, Target(*data[state_name]))
         return 'done'
 
-ModuleInstallUpgrade()
-
 
 class ModuleConfig(Wizard):
     'Configure Modules'
-    _name = 'ir.module.module.config'
+    __name__ = 'ir.module.module.config'
 
     start = StateAction('ir.act_module_form')
 
-    def transition_start(self, session):
+    @staticmethod
+    def transition_start():
         return 'end'
-
-ModuleConfig()
diff -r fb51f5b4aa4c trytond/ir/property.py
--- a/trytond/ir/property.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/property.py	Mon Sep 24 20:36:36 2012 +0200
@@ -6,37 +6,42 @@
 from ..cache import Cache
 from ..pool import Pool
 
+__all__ = [
+    'Property',
+    ]
+
 
 class Property(ModelSQL, ModelView):
     "Property"
-    _name = 'ir.property'
-    _description = __doc__
+    __name__ = 'ir.property'
     _rec_name = 'field'
     value = fields.Reference('Value', selection='models_get')
     res = fields.Reference('Resource', selection='models_get', select=True)
     field = fields.Many2One('ir.model.field', 'Field',
         ondelete='CASCADE', required=True, select=True)
+    _models_get_cache = Cache('ir_property.models_get', context=False)
 
-    @Cache('ir_property.models_get')
-    def models_get(self):
+    @classmethod
+    def models_get(cls):
+        models = cls._models_get_cache.get(None)
+        if models:
+            return models
         cursor = Transaction().cursor
         cursor.execute('SELECT model, name FROM ir_model ORDER BY name ASC')
-        res = cursor.fetchall() + [('', '')]
-        return res
+        models = cursor.fetchall() + [('', '')]
+        cls._models_get_cache.set(None, models)
+        return models
 
-    def get(self, names, model, res_ids=None):
+    @classmethod
+    def get(cls, names, model, res_ids=None):
         """
-        Return property value for each res_ids
-        :param names: property name or a list of property names
-        :param model: object name
-        :param res_ids: a list of record ids
-        :return: a dictionary
+        Return named property values for each res_ids of model
         """
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
+        ModelAccess = pool.get('ir.model.access')
         res = {}
 
-        model_access_obj.check(model, 'read')
+        ModelAccess.check(model, 'read')
 
         names_list = True
         if not isinstance(names, list):
@@ -45,34 +50,29 @@
         if res_ids is None:
             res_ids = []
 
-        property_ids = self.search([
+        properties = cls.search([
             ('field.name', 'in', names),
             ['OR',
                 ('res', '=', None),
                 ('res', 'in', ['%s,%s' % (model, x) for x in res_ids]),
                 ],
             ], order=[])
-        properties = self.browse(property_ids)
 
         default_vals = dict((x, None) for x in names)
-        for property in (x for x in properties if not x.res):
-            value = property.value
+        for property_ in (x for x in properties if not x.res):
+            value = property_.value
             val = None
-            if value:
-                if value.split(',')[0]:
-                    try:
-                        val = int(value.split(',')[1]\
-                                .split(',')[0].strip('('))
-                    except ValueError:
-                        val = None
+            if value is not None:
+                if not isinstance(value, basestring):
+                    val = int(value)
                 else:
-                    if property.field.ttype == 'numeric':
+                    if property_.field.ttype == 'numeric':
                         val = Decimal(value.split(',')[1])
-                    elif property.field.ttype in ('char', 'selection'):
+                    elif property_.field.ttype in ('char', 'selection'):
                         val = value.split(',')[1]
                     else:
                         raise Exception('Not implemented')
-            default_vals[property.field.name] = val
+            default_vals[property_.field.name] = val
 
         if not res_ids:
             if not names_list:
@@ -82,80 +82,69 @@
         for name in names:
             res[name] = dict((x, default_vals[name]) for x in res_ids)
 
-        for property in (x for x in properties if x.res):
+        for property_ in (x for x in properties if x.res):
             val = None
-            if property.value:
-                if property.value.split(',')[0]:
-                    try:
-                        val = int(property.value.split(',')[1]\
-                                .split(',')[0].strip('('))
-                    except ValueError:
-                        val = None
+            if property_.value is not None:
+                if not isinstance(property_.value, basestring):
+                    val = int(property_.value)
                 else:
-                    if property.field.ttype == 'numeric':
-                        val = Decimal(property.value.split(',')[1])
-                    elif property.field.ttype in ('char', 'selection'):
-                        val = property.value.split(',')[1]
+                    if property_.field.ttype == 'numeric':
+                        val = Decimal(property_.value.split(',')[1])
+                    elif property_.field.ttype in ('char', 'selection'):
+                        val = property_.value.split(',')[1]
                     else:
                         raise Exception('Not implemented')
-            res[property.field.name][
-                int(property.res.split(',')[1].split(',')[0].strip('('))] = val
+            res[property_.field.name][int(property_.res)] = val
 
         if not names_list:
             return res[names[0]]
         return res
 
-    def _set_values(self, model, res_id, val, field_id):
+    @staticmethod
+    def _set_values(model, res_id, val, field_id):
         return {
             'value': val,
             'res': model + ',' + str(res_id),
             'field': field_id,
         }
 
-    def set(self, name, model, ids, val):
+    @classmethod
+    def set(cls, name, model, ids, val):
         """
-        Set property value for ids
-        :param name: property name
-        :param model: object name
-        :param ids: a list of ids
-        :param val: the value
-        :return: the id of the record created
+        Set named property value for ids of model
+        Return the id of the record created
         """
         pool = Pool()
-        model_field_obj = pool.get('ir.model.field')
-        model_access_obj = pool.get('ir.model.access')
+        ModelField = pool.get('ir.model.field')
+        ModelAccess = pool.get('ir.model.access')
 
-        model_access_obj.check(model, 'write')
+        ModelAccess.check(model, 'write')
 
-        field_id = model_field_obj.search([
+        model_field, = ModelField.search([
             ('name', '=', name),
             ('model.model', '=', model),
-            ], order=[], limit=1)[0]
-        model_obj = pool.get(model)
-        field = model_obj._columns[name]
+            ], order=[], limit=1)
+        Model = pool.get(model)
+        field = Model._fields[name]
 
-        property_ids = self.search([
-            ('field', '=', field_id),
+        properties = cls.search([
+            ('field', '=', model_field.id),
             ('res', 'in', [model + ',' + str(res_id) for res_id in ids]),
             ], order=[])
         with Transaction().set_user(0, set_context=True):
-            self.delete(property_ids)
+            cls.delete(properties)
 
-        default_id = self.search([
-            ('field', '=', field_id),
+        defaults = cls.search([
+            ('field', '=', model_field.id),
             ('res', '=', None),
             ], order=[], limit=1)
         default_val = None
-        if default_id:
-            value = self.browse(default_id[0]).value
+        if defaults:
+            value = cls(defaults[0].id).value
             default_val = None
-            if value:
-                if value.split(',')[0]:
-                    try:
-                        default_val = int(value.split(',')[1]\
-                                .split(',')[0].strip('('))
-                    except ValueError:
-                        default_val = None
+            if value is not None:
+                if not isinstance(value, basestring):
+                    default_val = int(value)
                 else:
                     if field._type == 'numeric':
                         default_val = Decimal(value.split(',')[1])
@@ -164,12 +153,8 @@
                     else:
                         raise Exception('Not implemented')
 
-        res = None
         if (val != default_val):
             for res_id in ids:
-                vals = self._set_values(model, res_id, val, field_id)
+                vals = cls._set_values(model, res_id, val, model_field.id)
                 with Transaction().set_user(0, set_context=True):
-                    res = self.create(vals)
-        return res
-
-Property()
+                    cls.create(vals).id
diff -r fb51f5b4aa4c trytond/ir/rule.py
--- a/trytond/ir/rule.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/rule.py	Mon Sep 24 20:36:36 2012 +0200
@@ -10,11 +10,14 @@
 from ..const import OPERATORS
 from ..pool import Pool
 
+__all__ = [
+    'RuleGroup', 'Rule',
+    ]
+
 
 class RuleGroup(ModelSQL, ModelView):
     "Rule group"
-    _name = 'ir.rule.group'
-    _description = __doc__
+    __name__ = 'ir.rule.group'
     name = fields.Char('Name', select=True)
     model = fields.Many2One('ir.model', 'Model', select=True,
             required=True)
@@ -34,60 +37,65 @@
     perm_create = fields.Boolean('Create Access')
     perm_delete = fields.Boolean('Delete Access')
 
-    def __init__(self):
-        super(RuleGroup, self).__init__()
-        self._order.insert(0, ('model', 'ASC'))
-        self._order.insert(1, ('global_p', 'ASC'))
-        self._order.insert(2, ('default_p', 'ASC'))
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(RuleGroup, cls).__setup__()
+        cls._order.insert(0, ('model', 'ASC'))
+        cls._order.insert(1, ('global_p', 'ASC'))
+        cls._order.insert(2, ('default_p', 'ASC'))
+        cls._sql_constraints += [
             ('global_default_exclusive', 'CHECK(NOT(global_p AND default_p))',
                 'Global and Default are mutually exclusive!'),
         ]
 
-    def default_global_p(self):
+    @staticmethod
+    def default_global_p():
         return True
 
-    def default_default_p(self):
+    @staticmethod
+    def default_default_p():
         return False
 
-    def default_perm_read(self):
+    @staticmethod
+    def default_perm_read():
         return True
 
-    def default_perm_write(self):
+    @staticmethod
+    def default_perm_write():
         return True
 
-    def default_perm_create(self):
+    @staticmethod
+    def default_perm_create():
         return True
 
-    def default_perm_delete(self):
+    @staticmethod
+    def default_perm_delete():
         return True
 
-    def delete(self, ids):
-        res = super(RuleGroup, self).delete(ids)
+    @classmethod
+    def delete(cls, groups):
+        super(RuleGroup, cls).delete(groups)
         # Restart the cache on the domain_get method of ir.rule
-        Pool().get('ir.rule').domain_get.reset()
+        Pool().get('ir.rule')._domain_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        res = super(RuleGroup, cls).create(vals)
+        # Restart the cache on the domain_get method of ir.rule
+        Pool().get('ir.rule')._domain_get_cache.clear()
         return res
 
-    def create(self, vals):
-        res = super(RuleGroup, self).create(vals)
+    @classmethod
+    def write(cls, groups, vals):
+        super(RuleGroup, cls).write(groups, vals)
         # Restart the cache on the domain_get method of ir.rule
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-    def write(self, ids, vals):
-        res = super(RuleGroup, self).write(ids, vals)
-        # Restart the cache on the domain_get method of ir.rule
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-RuleGroup()
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
 
 class Rule(ModelSQL, ModelView):
     "Rule"
-    _name = 'ir.rule'
+    __name__ = 'ir.rule'
     _rec_name = 'field'
-    _description = __doc__
     field = fields.Many2One('ir.model.field', 'Field',
         domain=[('model', '=', Eval('_parent_rule_group', {}).get('model'))],
         select=True, required=True)
@@ -96,18 +104,21 @@
     operand = fields.Selection('get_operand', 'Operand', required=True)
     rule_group = fields.Many2One('ir.rule.group', 'Group', select=True,
        required=True, ondelete="CASCADE")
+    _domain_get_cache = Cache('ir_rule.domain_get')
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
 
-        super(Rule, self).init(module_name)
+        super(Rule, cls).__register__(module_name)
 
         # Migration from 2.0: rename operator '<>' into '!='
         cursor.execute('UPDATE "%s" '
             'SET operator = %%s '
-            'WHERE operator = %%s' % self._table, ('!=', '<>'))
+            'WHERE operator = %%s' % cls._table, ('!=', '<>'))
 
-    def _operand_get(self, obj_name='', level=3, recur=None, root_tech='',
+    @classmethod
+    def _operand_get(cls, obj_name='', level=3, recur=None, root_tech='',
             root=''):
         res = {}
         if not obj_name:
@@ -123,33 +134,36 @@
 
             if obj_fields[k]['type'] in ('many2one'):
                 res[root + '/' + obj_fields[k]['string']] = (
-                    '(' + root_tech + '.' + k + '.id '
-                    'if ' + root_tech + ' else None)')
+                    '(%s.%s.id if %s.%s else None)'
+                    % (root_tech, k, root_tech, k))
 
             elif obj_fields[k]['type'] in ('many2many', 'one2many'):
-                res[root + '/' + obj_fields[k]['string']] = \
-                        '[x.id for x in ' + root_tech + '.' + k + ']'
+                res[root + '/' + obj_fields[k]['string']] = (
+                    '[x.id for x in (%s.%s or [])]'
+                    % (root_tech, k))
             else:
                 res[root + '/' + obj_fields[k]['string']] = \
-                        root_tech + '.' + k
+                    root_tech + '.' + k
 
             if (obj_fields[k]['type'] in recur) and (level > 0):
-                res.update(self._operand_get(obj_fields[k]['relation'],
-                    level - 1, recur, root_tech + '.' + k,
-                    root + '/' + obj_fields[k]['string']))
+                res.update(cls._operand_get(obj_fields[k]['relation'],
+                        level - 1, recur,
+                        '(%s.%s if %s else None)' % (root_tech,  k, root_tech),
+                        root + '/' + obj_fields[k]['string']))
 
         return res
 
-    def get_operand(self):
+    @classmethod
+    def get_operand(cls):
         res = []
-        operands = self._operand_get('res.user', level=1, recur=['many2one'],
+        operands = cls._operand_get('res.user', level=1, recur=['many2one'],
                 root_tech='user', root='User')
         for i in operands.keys():
             res.append((i, i))
         return res
 
-    @Cache('ir_rule.domain_get')
-    def domain_get(self, model_name, mode='read'):
+    @classmethod
+    def domain_get(cls, model_name, mode='read'):
         assert mode in ['read', 'write', 'create', 'delete'], \
                 'Invalid domain mode for security'
 
@@ -158,30 +172,35 @@
             if not Transaction().context.get('user'):
                 return '', []
             with Transaction().set_user(Transaction().context['user']):
-                return self.domain_get(model_name, mode=mode)
+                return cls.domain_get(model_name, mode=mode)
+
+        key = (model_name, mode)
+        domain = cls._domain_get_cache.get(key)
+        if domain:
+            return domain
 
         pool = Pool()
-        rule_group_obj = pool.get('ir.rule.group')
-        model_obj = pool.get('ir.model')
-        rule_group_user_obj = pool.get('ir.rule.group-res.user')
-        rule_group_group_obj = pool.get('ir.rule.group-res.group')
-        user_group_obj = pool.get('res.user-res.group')
-        user_obj = pool.get('res.user')
+        RuleGroup = pool.get('ir.rule.group')
+        Model = pool.get('ir.model')
+        RuleGroup_User = pool.get('ir.rule.group-res.user')
+        RuleGroup_Group = pool.get('ir.rule.group-res.group')
+        User_Group = pool.get('res.user-res.group')
+        User = pool.get('res.user')
 
         cursor = Transaction().cursor
-        cursor.execute('SELECT r.id FROM "' + self._table + '" r '
-            'JOIN "' + rule_group_obj._table + '" g '
+        cursor.execute('SELECT r.id FROM "' + cls._table + '" r '
+            'JOIN "' + RuleGroup._table + '" g '
                 "ON (g.id = r.rule_group) "
-            'JOIN "' + model_obj._table + '" m ON (g.model = m.id) '
+            'JOIN "' + Model._table + '" m ON (g.model = m.id) '
             "WHERE m.model = %s "
                 "AND g.perm_" + mode + " "
                 "AND (g.id IN ("
                         'SELECT rule_group '
-                        'FROM "' + rule_group_user_obj._table + '" '
+                        'FROM "' + RuleGroup_User._table + '" '
                         'WHERE "user" = %s '
                         "UNION SELECT rule_group "
-                        'FROM "' + rule_group_group_obj._table + '" g_rel '
-                        'JOIN "' + user_group_obj._table + '" u_rel '
+                        'FROM "' + RuleGroup_Group._table + '" g_rel '
+                        'JOIN "' + User_Group._table + '" u_rel '
                             'ON (g_rel."group" = u_rel."group") '
                         'WHERE u_rel."user" = %s) '
                     "OR default_p "
@@ -189,19 +208,20 @@
                 (model_name, Transaction().user, Transaction().user))
         ids = [x[0] for x in cursor.fetchall()]
         if not ids:
+            cls._domain_get_cache.set(key, ('', []))
             return '', []
         obj = pool.get(model_name)
         clause = {}
         clause_global = {}
-        operand2query = self._operand_get('res.user', level=1,
+        operand2query = cls._operand_get('res.user', level=1,
                 recur=['many2one'], root_tech='user', root='User')
         user_id = Transaction().user
         with Transaction().set_user(0, set_context=True):
-            user = user_obj.browse(user_id)
+            user = User(user_id)
         # Use root user without context to prevent recursion
         with contextlib.nested(Transaction().set_user(0),
                 Transaction().set_context(user=0)):
-            for rule in self.browse(ids):
+            for rule in cls.browse(ids):
                 dom = safe_eval("[('%s', '%s', %s)]" % \
                         (rule.field.name, rule.operator,
                             operand2query[rule.operand]), {
@@ -209,7 +229,7 @@
                                 'time': time,
                                 })
 
-                if rule.rule_group['global_p']:
+                if rule.rule_group.global_p:
                     clause_global.setdefault(rule.rule_group.id, ['OR'])
                     clause_global[rule.rule_group.id].append(dom)
                 else:
@@ -220,17 +240,17 @@
         val = []
 
         # Test if there is no rule_group that have no rule
-        cursor.execute('SELECT g.id FROM "' + rule_group_obj._table + '" g ' \
-                'JOIN "' + model_obj._table + '" m ON (g.model = m.id) ' \
+        cursor.execute('SELECT g.id FROM "' + RuleGroup._table + '" g ' \
+                'JOIN "' + Model._table + '" m ON (g.model = m.id) ' \
             'WHERE m.model = %s ' \
                 'AND (g.id NOT IN (SELECT rule_group ' \
-                        'FROM "' + self._table + '")) ' \
+                        'FROM "' + cls._table + '")) ' \
                 'AND (g.id IN (SELECT rule_group ' \
-                        'FROM "' + rule_group_user_obj._table + '" ' \
+                        'FROM "' + RuleGroup_User._table + '" ' \
                         'WHERE "user" = %s ' \
                         'UNION SELECT rule_group ' \
-                        'FROM "' + rule_group_group_obj._table + '" g_rel ' \
-                            'JOIN "' + user_group_obj._table + '" u_rel ' \
+                        'FROM "' + RuleGroup_Group._table + '" g_rel ' \
+                            'JOIN "' + User_Group._table + '" u_rel ' \
                                 'ON g_rel."group" = u_rel."group" ' \
                         'WHERE u_rel."user" = %s))',
             (model_name, user_id, user_id))
@@ -249,24 +269,24 @@
 
         query, val, _, _ = obj.search_domain(clause, active_test=False)
 
+        cls._domain_get_cache.set(key, (query, val))
         return query, val
 
-    def delete(self, ids):
-        res = super(Rule, self).delete(ids)
+    @classmethod
+    def delete(cls, rules):
+        super(Rule, cls).delete(rules)
         # Restart the cache on the domain_get method of ir.rule
-        self.domain_get.reset()
+        cls._domain_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        res = super(Rule, cls).create(vals)
+        # Restart the cache on the domain_get method of ir.rule
+        cls._domain_get_cache.clear()
         return res
 
-    def create(self, vals):
-        res = super(Rule, self).create(vals)
-        # Restart the cache on the domain_get method of ir.rule
-        self.domain_get.reset()
-        return res
-
-    def write(self, ids, vals):
-        res = super(Rule, self).write(ids, vals)
+    @classmethod
+    def write(cls, rules, vals):
+        super(Rule, cls).write(rules, vals)
         # Restart the cache on the domain_get method
-        self.domain_get.reset()
-        return res
-
-Rule()
+        cls._domain_get_cache.clear()
diff -r fb51f5b4aa4c trytond/ir/sequence.py
--- a/trytond/ir/sequence.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/sequence.py	Mon Sep 24 20:36:36 2012 +0200
@@ -10,23 +10,25 @@
 from ..config import CONFIG
 from ..backend import TableHandler
 
+__all__ = [
+    'SequenceType', 'Sequence', 'SequenceStrict',
+    ]
+
 sql_sequence = CONFIG.options['db_type'] == 'postgresql'
 
 
 class SequenceType(ModelSQL, ModelView):
     "Sequence type"
-    _name = 'ir.sequence.type'
-    _description = __doc__
+    __name__ = 'ir.sequence.type'
+
     name = fields.Char('Sequence Name', required=True, translate=True)
     code = fields.Char('Sequence Code', required=True)
 
-SequenceType()
-
 
 class Sequence(ModelSQL, ModelView):
     "Sequence"
-    _name = 'ir.sequence'
-    _description = __doc__
+    __name__ = 'ir.sequence'
+
     _strict = False
     name = fields.Char('Sequence Name', required=True, translate=True)
     code = fields.Selection('code_get', 'Sequence Code', required=True,
@@ -77,188 +79,197 @@
                 ['decimal timestamp', 'hexadecimal timestamp']),
             }, depends=['type'])
 
-    def __init__(self):
-        super(Sequence, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Sequence, cls).__setup__()
+        cls._constraints += [
             ('check_prefix_suffix', 'invalid_prefix_suffix'),
             ('check_last_timestamp', 'future_last_timestamp'),
         ]
-        self._sql_constraints += [
+        cls._sql_constraints += [
             ('check_timestamp_rounding', 'CHECK(timestamp_rounding > 0)',
                 'Timestamp rounding should be greater than 0'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'missing': 'Missing sequence!',
             'invalid_prefix_suffix': 'Invalid prefix/suffix!',
             'future_last_timestamp': 'Last Timestamp could not be in future!',
             })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 2.0 rename number_next into number_next_internal
         table.column_rename('number_next', 'number_next_internal')
 
-        super(Sequence, self).init(module_name)
+        super(Sequence, cls).__register__(module_name)
 
         # Migration from 2.0 create sql_sequence
-        if sql_sequence and not self._strict:
-            sequence_ids = self.search([])
-            for sequence in self.browse(sequence_ids):
+        if sql_sequence and not cls._strict:
+            sequences = cls.search([])
+            for sequence in sequences:
                 if sequence.type != 'incremental':
                     continue
                 if not TableHandler.sequence_exist(cursor,
-                        self._sql_sequence_name(sequence)):
-                    self.create_sql_sequence(sequence,
-                        sequence.number_next_internal)
+                        sequence._sql_sequence_name):
+                    sequence.create_sql_sequence(sequence.number_next_internal)
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'incremental'
 
-    def default_number_increment(self):
+    @staticmethod
+    def default_number_increment():
         return 1
 
-    def default_number_next(self):
+    @staticmethod
+    def default_number_next():
         return 1
 
-    def default_padding(self):
+    @staticmethod
+    def default_padding():
         return 0
 
-    def default_timestamp_rounding(self):
+    @staticmethod
+    def default_timestamp_rounding():
         return 1.0
 
-    def default_timestamp_offset(self):
+    @staticmethod
+    def default_timestamp_offset():
         return 946681200.0  # Offset for 2000-01-01
 
-    def default_last_timestamp(self):
+    @staticmethod
+    def default_last_timestamp():
         return 0.0
 
-    def default_code(self):
+    @staticmethod
+    def default_code():
         return Transaction().context.get('code')
 
-    def get_number_next(self, ids, name):
+    def get_number_next(self, name):
         cursor = Transaction().cursor
-        result = {}
-        for sequence in self.browse(ids):
-            sql_name = self._sql_sequence_name(sequence)
-            if sql_sequence and not self._strict:
-                cursor.execute('SELECT '
-                    'CASE WHEN NOT is_called THEN last_value '
-                        'ELSE last_value + increment_by '
-                    'END FROM "%s"' % sql_name)
-                value, = cursor.fetchone()
-            else:
-                value = sequence.number_next_internal
-            result[sequence.id] = value
-        return result
+        sql_name = self._sql_sequence_name
+        if sql_sequence and not self._strict:
+            cursor.execute('SELECT '
+                'CASE WHEN NOT is_called THEN last_value '
+                    'ELSE last_value + increment_by '
+                'END FROM "%s"' % sql_name)
+            return cursor.fetchone()[0]
+        else:
+            return self.number_next_internal
 
-    def set_number_next(self, ids, name, value):
-        super(Sequence, self).write(ids, {
+    @classmethod
+    def set_number_next(cls, sequences, name, value):
+        super(Sequence, cls).write(sequences, {
                 'number_next_internal': value,
                 })
 
-    def create(self, values):
-        sequence_id = super(Sequence, self).create(values)
-        if sql_sequence and not self._strict:
-            sequence = self.browse(sequence_id)
-            self.update_sql_sequence(sequence, values.get('number_next',
-                    self.default_number_next()))
+    @classmethod
+    def create(cls, values):
+        sequence_id = super(Sequence, cls).create(values)
+        if sql_sequence and not cls._strict:
+            sequence = cls(sequence_id)
+            sequence.update_sql_sequence(values.get('number_next',
+                    cls.default_number_next()))
         return sequence_id
 
-    def write(self, ids, values):
-        result = super(Sequence, self).write(ids, values)
-        if sql_sequence and not self._strict:
-            ids = [ids] if isinstance(ids, (int, long)) else ids
-            sequences = self.browse(ids)
+    @classmethod
+    def write(cls, sequences, values):
+        result = super(Sequence, cls).write(sequences, values)
+        if sql_sequence and not cls._strict:
             for sequence in sequences:
-                self.update_sql_sequence(sequence, values.get('number_next'))
+                sequence.update_sql_sequence(values.get('number_next'))
         return result
 
-    def delete(self, ids):
-        if sql_sequence and not self._strict:
-            ids = [ids] if isinstance(ids, (int, long)) else ids
-            sequences = self.browse(ids)
+    @classmethod
+    def delete(cls, sequences):
+        if sql_sequence and not cls._strict:
             for sequence in sequences:
-                self.delete_sql_sequence(sequence)
-        return super(Sequence, self).delete(ids)
+                sequence.delete_sql_sequence()
+        return super(Sequence, cls).delete(sequences)
 
-    def code_get(self):
+    @classmethod
+    def code_get(cls):
         pool = Pool()
-        sequence_type_obj = pool.get('ir.sequence.type')
-        sequence_type_ids = sequence_type_obj.search([])
-        sequence_types = sequence_type_obj.browse(sequence_type_ids)
+        SequenceType = pool.get('ir.sequence.type')
+        sequence_types = SequenceType.search([])
         return [(x.code, x.name) for x in sequence_types]
 
-    def check_prefix_suffix(self, ids):
+    @classmethod
+    def check_prefix_suffix(cls, sequences):
         "Check prefix and suffix"
 
-        for sequence in self.browse(ids):
+        for sequence in sequences:
             try:
-                self._process(sequence.prefix)
-                self._process(sequence.suffix)
+                cls._process(sequence.prefix)
+                cls._process(sequence.suffix)
             except Exception:
                 return False
         return True
 
-    def check_last_timestamp(self, ids):
+    @classmethod
+    def check_last_timestamp(cls, sequences):
         "Check last_timestamp"
 
-        for sequence in self.browse(ids):
-            next_timestamp = self._timestamp(sequence)
+        for sequence in sequences:
+            next_timestamp = cls._timestamp(sequence)
             if sequence.last_timestamp > next_timestamp:
                 return False
         return True
 
-    def _sql_sequence_name(self, sequence):
+    @property
+    def _sql_sequence_name(self):
         'Return SQL sequence name'
-        return '%s_%s' % (self._table, sequence.id)
+        return '%s_%s' % (self._table, self.id)
 
-    def create_sql_sequence(self, sequence, number_next=None):
+    def create_sql_sequence(self, number_next=None):
         'Create the SQL sequence'
         cursor = Transaction().cursor
-        if sequence.type != 'incremental':
+        if self.type != 'incremental':
             return
         if number_next is None:
-            number_next = sequence.number_next
-        cursor.execute('CREATE SEQUENCE "' + self._sql_sequence_name(sequence)
-            + '" INCREMENT BY %s START WITH %s', (sequence.number_increment,
-                number_next))
+            number_next = self.number_next
+        cursor.execute('CREATE SEQUENCE "' + self._sql_sequence_name
+            + '" INCREMENT BY %s START WITH %s',
+            (self.number_increment, number_next))
 
-    def update_sql_sequence(self, sequence, number_next=None):
+    def update_sql_sequence(self, number_next=None):
         'Update the SQL sequence'
         cursor = Transaction().cursor
-        exist = TableHandler.sequence_exist(cursor,
-            self._sql_sequence_name(sequence))
-        if sequence.type != 'incremental':
+        exist = TableHandler.sequence_exist(cursor, self._sql_sequence_name)
+        if self.type != 'incremental':
             if exist:
-                self.delete_sql_sequence(sequence)
+                self.delete_sql_sequence()
             return
         if not exist:
-            self.create_sql_sequence(sequence, number_next)
+            self.create_sql_sequence(number_next)
             return
         if number_next is None:
-            number_next = sequence.number_next
-        cursor.execute('ALTER SEQUENCE "' + self._sql_sequence_name(sequence)
-            + '" INCREMENT BY %s RESTART WITH %s', (sequence.number_increment,
-                number_next))
+            number_next = self.number_next
+        cursor.execute('ALTER SEQUENCE "' + self._sql_sequence_name
+            + '" INCREMENT BY %s RESTART WITH %s',
+            (self.number_increment, number_next))
 
-    def delete_sql_sequence(self, sequence):
+    def delete_sql_sequence(self):
         'Delete the SQL sequence'
         cursor = Transaction().cursor
-        if sequence.type != 'incremental':
+        if self.type != 'incremental':
             return
         cursor.execute('DROP SEQUENCE "%s"'
-            % self._sql_sequence_name(sequence))
+            % self._sql_sequence_name)
 
-    def _process(self, string, date=None):
+    @staticmethod
+    def _process(string, date=None):
         pool = Pool()
-        date_obj = pool.get('ir.date')
+        Date = pool.get('ir.date')
         if not date:
-            date = date_obj.today()
+            date = Date.today()
         year = datetime_strftime(date, '%Y')
         month = datetime_strftime(date, '%m')
         day = datetime_strftime(date, '%d')
@@ -268,23 +279,25 @@
                 day=day,
                 )
 
-    def _timestamp(self, sequence):
+    @staticmethod
+    def _timestamp(sequence):
         return int((time.time() - sequence.timestamp_offset)
                 / sequence.timestamp_rounding)
 
-    def _get_sequence(self, sequence):
+    @classmethod
+    def _get_sequence(cls, sequence):
         if sequence.type == 'incremental':
-            if sql_sequence and not self._strict:
+            if sql_sequence and not cls._strict:
                 cursor = Transaction().cursor
                 cursor.execute('SELECT nextval(\'"%s"\')'
-                    % self._sql_sequence_name(sequence))
+                    % sequence._sql_sequence_name)
                 number_next, = cursor.fetchone()
             else:
                 #Pre-fetch number_next
                 number_next = sequence.number_next_internal
 
                 with Transaction().set_user(0):
-                    self.write(sequence.id, {
+                    cls.write([sequence], {
                             'number_next_internal': (number_next
                                 + sequence.number_increment),
                             })
@@ -292,9 +305,9 @@
         elif sequence.type in ('decimal timestamp', 'hexadecimal timestamp'):
             timestamp = sequence.last_timestamp
             while timestamp == sequence.last_timestamp:
-                timestamp = self._timestamp(sequence)
+                timestamp = cls._timestamp(sequence)
             with Transaction().set_user(0):
-                self.write(sequence.id, {
+                cls.write([sequence], {
                     'last_timestamp': timestamp,
                     })
             if sequence.type == 'decimal timestamp':
@@ -303,45 +316,42 @@
                 return hex(timestamp)[2:].upper()
         return ''
 
-    def get_id(self, domain):
+    @classmethod
+    def get_id(cls, domain):
         '''
         Return sequence value for the domain
-
-        :param domain: a domain or a sequence id
-        :return: the sequence value
         '''
+        if isinstance(domain, cls):
+            domain = domain.id
         if isinstance(domain, (int, long)):
             domain = [('id', '=', domain)]
 
         # bypass rules on sequences
         with Transaction().set_context(user=False):
             with Transaction().set_user(0):
-                sequence_ids = self.search(domain, limit=1)
+                try:
+                    sequence, = cls.search(domain, limit=1)
+                except TypeError:
+                    cls.raise_user_error('missing')
             date = Transaction().context.get('date')
-            if sequence_ids:
-                with Transaction().set_user(0):
-                    sequence = self.browse(sequence_ids[0])
-                return '%s%s%s' % (
-                        self._process(sequence.prefix, date=date),
-                        self._get_sequence(sequence),
-                        self._process(sequence.suffix, date=date),
-                        )
-        self.raise_user_error('missing')
+            return '%s%s%s' % (
+                cls._process(sequence.prefix, date=date),
+                cls._get_sequence(sequence),
+                cls._process(sequence.suffix, date=date),
+                )
 
-    def get(self, code):
-        return self.get_id([('code', '=', code)])
-
-Sequence()
+    @classmethod
+    def get(cls, code):
+        return cls.get_id([('code', '=', code)])
 
 
 class SequenceStrict(Sequence):
     "Sequence Strict"
-    _name = 'ir.sequence.strict'
-    _description = __doc__
+    __name__ = 'ir.sequence.strict'
+    _table = 'ir_sequence_strict'  # Needed to override Sequence._table
     _strict = True
 
-    def get_id(self, clause):
-        Transaction().cursor.lock(self._table)
-        return super(SequenceStrict, self).get_id(clause)
-
-SequenceStrict()
+    @classmethod
+    def get_id(cls, clause):
+        Transaction().cursor.lock(cls._table)
+        return super(SequenceStrict, cls).get_id(clause)
diff -r fb51f5b4aa4c trytond/ir/session.py
--- a/trytond/ir/session.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/session.py	Mon Sep 24 20:36:36 2012 +0200
@@ -12,36 +12,42 @@
 from ..backend import TableHandler
 from ..transaction import Transaction
 
+__all__ = [
+    'Session', 'SessionWizard',
+    ]
+
 
 class Session(ModelSQL):
     "Session"
-    _name = 'ir.session'
-    _description = __doc__
+    __name__ = 'ir.session'
     _rec_name = 'key'
 
     key = fields.Char('Key', required=True, select=True)
 
-    def __init__(self):
-        super(Session, self).__init__()
-        self._rpc = {}
+    @classmethod
+    def __setup__(cls):
+        super(Session, cls).__setup__()
+        cls.__rpc__ = {}
 
-    def init(self, module_name):
-        super(Session, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Session, cls).__register__(module_name)
 
-        table = TableHandler(Transaction().cursor, self, module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action('create_uid', 'add')
 
-    def default_key(self):
+    @staticmethod
+    def default_key():
         return uuid.uuid4().hex
 
-    def check(self, user, key):
+    @classmethod
+    def check(cls, user, key):
         "Check user key and delete old one"
         now = datetime.datetime.now()
         timeout = datetime.timedelta(seconds=int(CONFIG['session_timeout']))
-        session_ids = self.search([
+        sessions = cls.search([
                 ('create_uid', '=', user),
                 ])
-        sessions = self.browse(session_ids)
         find = False
         for session in sessions:
             timestamp = session.write_date or session.create_date
@@ -49,31 +55,29 @@
                 if session.key == key:
                     find = True
             else:
-                self.delete(session.id)
+                cls.delete([session])
         return find
 
-    def reset(self, session):
+    @classmethod
+    def reset(cls, session):
         "Reset session timestamp"
-        session_id = self.search([
+        sessions = cls.search([
                 ('key', '=', session),
                 ])
-        self.write(session_id, {})
-
-Session()
+        cls.write(sessions, {})
 
 
 class SessionWizard(ModelSQL):
     "Session Wizard"
-    _name = 'ir.session.wizard'
-    _description = __doc__
+    __name__ = 'ir.session.wizard'
 
     data = fields.Text('Data')
 
-    def __init__(self):
-        super(SessionWizard, self).__init__()
-        self._rpc = {}
+    @classmethod
+    def __setup__(cls):
+        super(SessionWizard, cls).__setup__()
+        cls.__rpc__ = {}
 
-    def default_data(self):
+    @staticmethod
+    def default_data():
         return json.dumps({})
-
-SessionWizard()
diff -r fb51f5b4aa4c trytond/ir/translation.py
--- a/trytond/ir/translation.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/translation.py	Mon Sep 24 20:36:36 2012 +0200
@@ -17,7 +17,6 @@
 from functools import reduce
 from ..config import CONFIG
 from ..model import ModelView, ModelSQL, fields
-from ..model.cacheable import Cacheable
 from ..wizard import Wizard, StateView, StateTransition, StateAction, \
     Button
 from ..tools import file_open, reduce_ids
@@ -25,6 +24,15 @@
 from ..pyson import PYSONEncoder
 from ..transaction import Transaction
 from ..pool import Pool
+from ..cache import Cache
+
+__all__ = ['Translation',
+    'ReportTranslationSetStart', 'ReportTranslationSetSucceed',
+    'ReportTranslationSet',
+    'TranslationCleanStart', 'TranslationCleanSucceed', 'TranslationClean',
+    'TranslationUpdateStart', 'TranslationUpdate',
+    'TranslationExportStart', 'TranslationExportResult', 'TranslationExport',
+    ]
 
 TRANSLATION_TYPE = [
     ('field', 'Field'),
@@ -45,10 +53,10 @@
             key=lambda x: (x.msgctxt, x.msgid))
 
 
-class Translation(ModelSQL, ModelView, Cacheable):
+class Translation(ModelSQL, ModelView):
     "Translation"
-    _name = "ir.translation"
-    _description = __doc__
+    __name__ = "ir.translation"
+
     name = fields.Char('Field Name', required=True)
     res_id = fields.Integer('Resource ID', select=True)
     lang = fields.Selection('get_language', string='Language')
@@ -61,98 +69,103 @@
     fuzzy = fields.Boolean('Fuzzy')
     model = fields.Function(fields.Char('Model'), 'get_model',
             searcher='search_model')
+    _translation_cache = Cache('ir.translation')
 
-    def __init__(self):
-        super(Translation, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Translation, cls).__setup__()
+        cls._sql_constraints += [
             ('translation_md5_uniq',
                 'UNIQUE (name, res_id, lang, type, src_md5, module)',
                 'Translation must be unique'),
         ]
-        self._max_len = 10240
+        cls._max_len = 10240
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         # Migration from 1.8: new field src_md5
         src_md5_exist = table.column_exist('src_md5')
         if not src_md5_exist:
             table.add_raw_column('src_md5',
-                FIELDS[self.src_md5._type].sql_type(self.src_md5),
-                FIELDS[self.src_md5._type].sql_format, None,
-                self.src_md5.size, string=self.src_md5.string)
+                FIELDS[cls.src_md5._type].sql_type(cls.src_md5),
+                FIELDS[cls.src_md5._type].sql_format, None,
+                cls.src_md5.size, string=cls.src_md5.string)
         table.drop_constraint('translation_uniq')
         table.index_action(['lang', 'type', 'name', 'src'], 'remove')
 
-        super(Translation, self).init(module_name)
+        super(Translation, cls).__register__(module_name)
 
         # Migration from 1.8: fill new field src_md5
         if not src_md5_exist:
             offset = 0
             limit = cursor.IN_MAX
-            translation_ids = True
-            while translation_ids:
-                translation_ids = self.search([], offset=offset, limit=limit)
+            translations = True
+            while translations:
+                translations = cls.search([], offset=offset, limit=limit)
                 offset += limit
-                for translation in self.browse(translation_ids):
-                    src_md5 = self.get_src_md5(translation.src)
-                    self.write(translation.id, {
+                for translation in cls.browse(translations):
+                    src_md5 = cls.get_src_md5(translation.src)
+                    cls.write(translation.id, {
                         'src_md5': src_md5,
                     })
-            table = TableHandler(cursor, self, module_name)
+            table = TableHandler(cursor, cls, module_name)
             table.not_null_action('src_md5', action='add')
 
         # Migration from 2.2
-        cursor.execute("UPDATE " + self._table + " "
+        cursor.execute("UPDATE " + cls._table + " "
             "SET res_id = %s "
             "WHERE res_id = %s",
             (None, 0))
 
-        table = TableHandler(Transaction().cursor, self, module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action(['lang', 'type', 'name'], 'add')
 
-    def default_fuzzy(self):
+    @staticmethod
+    def default_fuzzy():
         return False
 
-    def get_model(self, ids, name):
-        res = {}
-        for translation in self.browse(ids):
-            res[translation.id] = translation.name.split(',')[0]
-        return res
+    def get_model(self, name):
+        return self.name.split(',')[0]
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         clause = tuple(clause)
-        ids = self.search(['OR',
+        translations = cls.search(['OR',
                 ('src',) + clause[1:],
                 ('value',) + clause[1:],
                 ])
-        if ids:
-            return [('id', 'in', ids)]
-        return [(self._rec_name,) + clause[1:]]
+        if translations:
+            return [('id', 'in', [t.id for t in translations])]
+        return [(cls._rec_name,) + clause[1:]]
 
-    def search_model(self, name, clause):
+    @classmethod
+    def search_model(cls, name, clause):
         cursor = Transaction().cursor
         cursor.execute('SELECT id FROM "%s" '
             'WHERE split_part(name, \',\', 1) %s %%s' %
-            (self._table, clause[1]), (clause[2],))
+            (cls._table, clause[1]), (clause[2],))
         return [('id', 'in', [x[0] for x in cursor.fetchall()])]
 
-    def get_language(self):
+    @classmethod
+    def get_language(cls):
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
-        lang_ids = lang_obj.search([])
-        langs = lang_obj.browse(lang_ids)
+        Lang = pool.get('ir.lang')
+        langs = Lang.search([])
         res = [(lang.code, lang.name) for lang in langs]
         return res
 
-    def get_src_md5(self, src):
+    @classmethod
+    def get_src_md5(cls, src):
         return md5((src or '').encode('utf-8')).hexdigest()
 
-    def get_ids(self, name, ttype, lang, ids):
+    @classmethod
+    def get_ids(cls, name, ttype, lang, ids):
         "Return translation for each id"
         pool = Pool()
-        model_fields_obj = pool.get('ir.model.field')
-        model_obj = pool.get('ir.model')
+        ModelFields = pool.get('ir.model.field')
+        Model = pool.get('ir.model')
 
         translations, to_fetch = {}, []
         name = unicode(name)
@@ -166,10 +179,10 @@
                         ttype = u'field'
                     else:
                         ttype = u'help'
-                    records = model_fields_obj.browse(ids)
+                    records = ModelFields.browse(ids)
                 else:
                     ttype = u'model'
-                    records = model_obj.browse(ids)
+                    records = Model.browse(ids)
 
             trans_args = []
             for record in records:
@@ -178,20 +191,20 @@
                 else:
                     name = record.model + ',' + field_name
                 trans_args.append((name, ttype, lang, None))
-            self.get_sources(trans_args)
+            cls.get_sources(trans_args)
 
             for record in records:
                 if ttype in ('field', 'help'):
                     name = record.model.model + ',' + record.name
                 else:
                     name = record.model + ',' + field_name
-                translations[record.id] = self.get_source(name, ttype, lang)
+                translations[record.id] = cls.get_source(name, ttype, lang)
             return translations
         # Don't use cache for fuzzy translation
         if not Transaction().context.get(
                 'fuzzy_translation', False):
             for obj_id in ids:
-                trans = self.get((lang, ttype, name, obj_id))
+                trans = cls._translation_cache.get((lang, ttype, name, obj_id))
                 if trans is not None:
                     translations[obj_id] = trans
                 else:
@@ -222,19 +235,21 @@
                     # Don't store fuzzy translation in cache
                     if not Transaction().context.get(
                             'fuzzy_translation', False):
-                        self.add((lang, ttype, name, res_id), value)
+                        cls._translation_cache.set((lang, ttype, name, res_id),
+                            value)
                     translations[res_id] = value
         for res_id in ids:
             if res_id not in translations:
-                self.add((lang, ttype, name, res_id), False)
+                cls._translation_cache.set((lang, ttype, name, res_id), False)
                 translations[res_id] = False
         return translations
 
-    def set_ids(self, name, ttype, lang, ids, value):
+    @classmethod
+    def set_ids(cls, name, ttype, lang, ids, value):
         "Set translation for each id"
         pool = Pool()
-        model_fields_obj = pool.get('ir.model.field')
-        model_obj = pool.get('ir.model')
+        ModelFields = pool.get('ir.model.field')
+        Model = pool.get('ir.model')
 
         model_name, field_name = name.split(',')
         if model_name in ('ir.model.field', 'ir.model'):
@@ -244,86 +259,87 @@
                 else:
                     ttype = 'help'
                 with Transaction().set_context(language='en_US'):
-                    records = model_fields_obj.browse(ids)
+                    records = ModelFields.browse(ids)
             else:
                 ttype = 'model'
                 with Transaction().set_context(language='en_US'):
-                    records = model_obj.browse(ids)
+                    records = Model.browse(ids)
             for record in records:
                 if ttype in ('field', 'help'):
                     name = record.model + ',' + record.name
-                ids2 = self.search([
+                translation2 = cls.search([
                     ('lang', '=', lang),
                     ('type', '=', ttype),
                     ('name', '=', name),
                     ])
                 with Transaction().set_user(0):
-                    if not ids2:
-                        self.create({
+                    if not translation2:
+                        cls.create({
                             'name': name,
                             'lang': lang,
                             'type': ttype,
-                            'src': record[field_name],
+                            'src': getattr(record, field_name),
                             'value': value,
                             'fuzzy': False,
                             })
                     else:
-                        self.write(ids, {
-                            'src': record[field_name],
+                        cls.write(translation2, {
+                            'src': getattr(record, field_name),
                             'value': value,
                             'fuzzy': False,
                             })
             return len(ids)
-        model_obj = pool.get(model_name)
+        Model = pool.get(model_name)
         with Transaction().set_context(language=CONFIG['language']):
-            records = model_obj.browse(ids)
+            records = Model.browse(ids)
         for record in records:
-            ids2 = self.search([
+            translation2 = cls.search([
                 ('lang', '=', lang),
                 ('type', '=', ttype),
                 ('name', '=', name),
                 ('res_id', '=', record.id),
                 ])
             with Transaction().set_user(0):
-                if not ids2:
-                    self.create({
+                if not translation2:
+                    cls.create({
                         'name': name,
                         'lang': lang,
                         'type': ttype,
                         'res_id': record.id,
                         'value': value,
-                        'src': record[field_name],
+                        'src': getattr(record, field_name),
                         'fuzzy': False,
                         })
                 else:
-                    self.write(ids2, {
+                    cls.write(translation2, {
                         'value': value,
-                        'src': record[field_name],
+                        'src': getattr(record, field_name),
                         'fuzzy': False,
                         })
                     if (lang == CONFIG['language']
                             and Transaction().context.get('fuzzy_translation',
                                 True)):
-                        other_lang_ids = self.search([
+                        other_langs = cls.search([
                                 ('lang', '!=', lang),
                                 ('type', '=', ttype),
                                 ('name', '=', name),
                                 ('res_id', '=', record.id),
                                 ])
-                        self.write(other_lang_ids, {
-                                'src': record[field_name],
+                        cls.write(other_langs, {
+                                'src': getattr(record, field_name),
                                 'fuzzy': True,
                                 })
         return len(ids)
 
-    def get_source(self, name, ttype, lang, source=None):
+    @classmethod
+    def get_source(cls, name, ttype, lang, source=None):
         "Return translation for source"
         name = unicode(name)
         ttype = unicode(ttype)
         lang = unicode(lang)
         if source is not None:
             source = unicode(source)
-        trans = self.get((lang, ttype, name, source))
+        trans = cls._translation_cache.get((lang, ttype, name, source))
         if trans is not None:
             return trans
 
@@ -353,13 +369,14 @@
                     (lang, ttype, str(name), False))
         res = cursor.fetchone()
         if res:
-            self.add((lang, ttype, name, source), res[0])
+            cls._translation_cache.set((lang, ttype, name, source), res[0])
             return res[0]
         else:
-            self.add((lang, ttype, name, source), False)
+            cls._translation_cache.set((lang, ttype, name, source), False)
             return None
 
-    def get_sources(self, args):
+    @classmethod
+    def get_sources(cls, args):
         '''
         Take a list of (name, ttype, lang, source).
         Add the translations to the cache.
@@ -371,7 +388,7 @@
         if len(args) > cursor.IN_MAX:
             for i in range(0, len(args), cursor.IN_MAX):
                 sub_args = args[i:i + cursor.IN_MAX]
-                res.update(self.get_sources(sub_args))
+                res.update(cls.get_sources(sub_args))
             return res
         for name, ttype, lang, source in args:
             name = unicode(name)
@@ -379,12 +396,12 @@
             lang = unicode(lang)
             if source is not None:
                 source = unicode(source)
-            trans = self.get((lang, ttype, name, source))
+            trans = cls._translation_cache.get((lang, ttype, name, source))
             if trans is not None:
                 res[(name, ttype, lang, source)] = trans
             else:
                 res[(name, ttype, lang, source)] = None
-                self.add((lang, ttype, name, source), None)
+                cls._translation_cache.set((lang, ttype, name, source), None)
                 if source is not None:
                     clause += [('(lang = %s ' \
                             'AND type = %s ' \
@@ -415,17 +432,20 @@
                     if (name, ttype, lang, source) not in args:
                         source = None
                     res[(name, ttype, lang, source)] = value
-                    self.add((lang, ttype, name, source), value)
+                    cls._translation_cache.set((lang, ttype, name, source),
+                        value)
         return res
 
-    def delete(self, ids):
-        self.clear()
-        self.fields_view_get.reset()
-        return super(Translation, self).delete(ids)
+    @classmethod
+    def delete(cls, translations):
+        cls._translation_cache.clear()
+        ModelView._fields_view_get_cache.clear()
+        return super(Translation, cls).delete(translations)
 
-    def create(self, vals):
-        self.clear()
-        self.fields_view_get.reset()
+    @classmethod
+    def create(cls, vals):
+        cls._translation_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         cursor = Transaction().cursor
         if not vals.get('module'):
             if Transaction().context.get('module'):
@@ -458,38 +478,40 @@
                     vals = vals.copy()
                     vals['module'], vals['src'] = fetchone
         vals = vals.copy()
-        vals['src_md5'] = self.get_src_md5(vals.get('src'))
-        return super(Translation, self).create(vals)
+        vals['src_md5'] = cls.get_src_md5(vals.get('src'))
+        return super(Translation, cls).create(vals)
 
-    def write(self, ids, vals):
-        self.clear()
-        self.fields_view_get.reset()
+    @classmethod
+    def write(cls, translations, vals):
+        cls._translation_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         if 'src' in vals:
             vals = vals.copy()
-            vals['src_md5'] = self.get_src_md5(vals.get('src'))
-        return super(Translation, self).write(ids, vals)
+            vals['src_md5'] = cls.get_src_md5(vals.get('src'))
+        return super(Translation, cls).write(translations, vals)
 
-    def translation_import(self, lang, module, po_path):
+    @classmethod
+    def translation_import(cls, lang, module, po_path):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        model_data_ids = model_data_obj.search([
-            ('module', '=', module),
-            ])
+        ModelData = pool.get('ir.model.data')
+        models_data = ModelData.search([
+                ('module', '=', module),
+                ])
         fs_id2model_data = {}
-        for model_data in model_data_obj.browse(model_data_ids):
+        for model_data in models_data:
             fs_id2model_data.setdefault(model_data.model, {})
             fs_id2model_data[model_data.model][model_data.fs_id] = model_data
 
-        translation_ids = []
+        translations = []
         pofile = polib.pofile(po_path)
 
         id2translation = {}
         key2ids = {}
-        module_translation_ids = self.search([
+        module_translations = cls.search([
             ('lang', '=', lang),
             ('module', '=', module),
             ])
-        for translation in self.browse(module_translation_ids):
+        for translation in module_translations:
             if translation.type in ('odt', 'view', 'wizard_button',
                     'selection', 'error'):
                 key = (translation.name, translation.res_id, translation.type,
@@ -534,7 +556,7 @@
             with contextlib.nested(Transaction().set_user(0),
                     Transaction().set_context(module=module)):
                 if not ids:
-                    translation_ids.append(self.create({
+                    translations.append(cls.create({
                         'name': name,
                         'res_id': res_id,
                         'lang': lang,
@@ -545,38 +567,39 @@
                         'module': module,
                         }))
                 else:
-                    ids2 = []
+                    translations2 = []
                     for translation_id in ids:
                         translation = id2translation[translation_id]
                         if translation.value != value \
                                 or translation.fuzzy != fuzzy:
-                            ids2.append(translation.id)
-                    if ids2 and not noupdate:
-                        self.write(ids2, {
+                            translations2.append(translation)
+                    if translations2 and not noupdate:
+                        cls.write(translations2, {
                             'value': value,
                             'fuzzy': fuzzy,
                             })
-                    translation_ids += ids
+                    translations += cls.browse(ids)
 
-        if translation_ids:
-            all_translation_ids = self.search([
+        if translations:
+            all_translations = cls.search([
                 ('module', '=', module),
                 ('lang', '=', lang),
                 ])
-            translation_ids_to_delete = [x for x in all_translation_ids
-                    if x not in translation_ids]
-            self.delete(translation_ids_to_delete)
-        return len(translation_ids)
+            translations_to_delete = [x for x in all_translations
+                    if x not in translations]
+            cls.delete(translations_to_delete)
+        return len(translations)
 
-    def translation_export(self, lang, module):
+    @classmethod
+    def translation_export(cls, lang, module):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
+        ModelData = pool.get('ir.model.data')
 
-        model_data_ids = model_data_obj.search([
-            ('module', '=', module),
-            ])
+        models_data = ModelData.search([
+                ('module', '=', module),
+                ])
         db_id2fs_id = {}
-        for model_data in model_data_obj.browse(model_data_ids):
+        for model_data in models_data:
             db_id2fs_id.setdefault(model_data.model, {})
             db_id2fs_id[model_data.model][model_data.db_id] = model_data.fs_id
 
@@ -586,14 +609,17 @@
             }
 
         with Transaction().set_context(language=CONFIG['language']):
-            translation_ids = self.search([
+            translations = cls.search([
                 ('lang', '=', lang),
                 ('module', '=', module),
                 ], order=[])
-        for translation in self.browse(translation_ids):
-            flags = [] if not translation['fuzzy'] else ['fuzzy']
-            trans_ctxt = '%(type)s:%(name)s:' % translation
-            res_id = translation['res_id']
+        for translation in translations:
+            flags = [] if not translation.fuzzy else ['fuzzy']
+            trans_ctxt = '%(type)s:%(name)s:' % {
+                'type': translation.type,
+                'name': translation.name,
+                }
+            res_id = translation.res_id
             if res_id:
                 model, _ = translation.name.split(',')
                 if model in db_id2fs_id:
@@ -609,28 +635,20 @@
         pofile.sort()
         return unicode(pofile).encode('utf-8')
 
-Translation()
-
 
 class ReportTranslationSetStart(ModelView):
     "Update Report Translation"
-    _name = 'ir.translation.set_report.start'
-    _description = __doc__
-
-ReportTranslationSetStart()
+    __name__ = 'ir.translation.set_report.start'
 
 
 class ReportTranslationSetSucceed(ModelView):
     "Update Report Translation"
-    _name = 'ir.translation.set_report.succeed'
-    _description = __doc__
-
-ReportTranslationSetSucceed()
+    __name__ = 'ir.translation.set_report.succeed'
 
 
 class ReportTranslationSet(Wizard):
     "Update report translation"
-    _name = "ir.translation.set_report"
+    __name__ = "ir.translation.set_report"
 
     start = StateView('ir.translation.set_report.start',
         'ir.translation_set_report_start_view_form', [
@@ -662,18 +680,17 @@
             strings.extend(self._translate_report(child))
         return strings
 
-    def transition_set_report(self, session):
+    def transition_set_report(self):
         pool = Pool()
-        report_obj = pool.get('ir.action.report')
-        translation_obj = pool.get('ir.translation')
+        Report = pool.get('ir.action.report')
+        Translation = pool.get('ir.translation')
 
         with Transaction().set_context(active_test=False):
-            report_ids = report_obj.search([])
+            reports = Report.search([])
 
-        if not report_ids:
+        if not reports:
             return {}
 
-        reports = report_obj.browse(report_ids)
         cursor = Transaction().cursor
         for report in reports:
             cursor.execute('SELECT id, name, src FROM ir_translation ' \
@@ -721,7 +738,7 @@
                 strings += self._translate_report(document.documentElement)
 
             for string in {}.fromkeys(strings).keys():
-                src_md5 = translation_obj.get_src_md5(string)
+                src_md5 = Translation.get_src_md5(string)
                 done = False
                 if string in trans_reports:
                     del trans_reports[string]
@@ -767,28 +784,20 @@
                                 tuple(strings))
         return 'succeed'
 
-ReportTranslationSet()
-
 
 class TranslationCleanStart(ModelView):
     'Clean translation'
-    _name = 'ir.translation.clean.start'
-    _description = __doc__
-
-TranslationCleanStart()
+    __name__ = 'ir.translation.clean.start'
 
 
 class TranslationCleanSucceed(ModelView):
     'Clean translation'
-    _name = 'ir.translation.clean.succeed'
-    _description = __doc__
-
-TranslationCleanSucceed()
+    __name__ = 'ir.translation.clean.succeed'
 
 
 class TranslationClean(Wizard):
     "Clean translation"
-    _name = 'ir.translation.clean'
+    __name__ = 'ir.translation.clean'
 
     start = StateView('ir.translation.clean.start',
         'ir.translation_clean_start_view_form', [
@@ -801,7 +810,8 @@
             Button('Ok', 'end', 'tryton-ok', default=True),
             ])
 
-    def _clean_field(self, translation):
+    @staticmethod
+    def _clean_field(translation):
         pool = Pool()
         try:
             model_name, field_name = translation.name.split(',', 1)
@@ -809,11 +819,12 @@
             return True
         if model_name not in pool.object_name_list():
             return True
-        model_obj = pool.get(model_name)
-        if field_name not in model_obj._columns:
+        Model = pool.get(model_name)
+        if field_name not in Model._fields:
             return True
 
-    def _clean_model(self, translation):
+    @staticmethod
+    def _clean_model(translation):
         pool = Pool()
         try:
             model_name, field_name = translation.name.split(',', 1)
@@ -822,26 +833,28 @@
         if model_name not in pool.object_name_list():
             return True
         if translation.res_id:
-            model_obj = pool.get(model_name)
-            if field_name not in model_obj._columns:
+            Model = pool.get(model_name)
+            if field_name not in Model._fields:
                 return True
-            field = model_obj._columns[field_name]
+            field = Model._fields[field_name]
             if (not hasattr(field, 'translate')
                     or not field.translate):
                 return True
         elif field_name not in ('name'):
             return True
 
-    def _clean_odt(self, translation):
+    @staticmethod
+    def _clean_odt(translation):
         pool = Pool()
-        report_obj = pool.get('ir.action.report')
+        Report = pool.get('ir.action.report')
         with Transaction().set_context(active_test=False):
-            if not report_obj.search([
+            if not Report.search([
                         ('report_name', '=', translation.name),
                         ]):
                 return True
 
-    def _clean_selection(self, translation):
+    @staticmethod
+    def _clean_selection(translation):
         pool = Pool()
         try:
             model_name, field_name = translation.name.split(',', 1)
@@ -849,10 +862,10 @@
             return True
         if model_name not in pool.object_name_list():
             return True
-        model_obj = pool.get(model_name)
-        if field_name not in model_obj._columns:
+        Model = pool.get(model_name)
+        if field_name not in Model._fields:
             return True
-        field = model_obj._columns[field_name]
+        field = Model._fields[field_name]
         if (not hasattr(field, 'selection')
                 or not field.selection
                 or not getattr(field, 'translate_selection', True)):
@@ -861,13 +874,15 @@
                 and translation.src not in dict(field.selection).values()):
             return True
 
-    def _clean_view(self, translation):
+    @staticmethod
+    def _clean_view(translation):
         pool = Pool()
         model_name = translation.name
         if model_name not in pool.object_name_list():
             return True
 
-    def _clean_wizard_button(self, translation):
+    @staticmethod
+    def _clean_wizard_button(translation):
         pool = Pool()
         try:
             wizard_name, state_name, button_name = \
@@ -877,17 +892,18 @@
         if (wizard_name not in
                 pool.object_name_list(type='wizard')):
             return True
-        wizard = pool.get(wizard_name, type='wizard')
-        if not wizard:
+        Wizard = pool.get(wizard_name, type='wizard')
+        if not Wizard:
             return True
-        state = wizard.states.get(state_name)
+        state = Wizard.states.get(state_name)
         if not state or not hasattr(state, 'buttons'):
             return True
         if button_name in [b.state for b in state.buttons]:
             return False
         return True
 
-    def _clean_help(self, translation):
+    @staticmethod
+    def _clean_help(translation):
         pool = Pool()
         try:
             model_name, field_name = translation.name.split(',', 1)
@@ -895,13 +911,14 @@
             return True
         if model_name not in pool.object_name_list():
             return True
-        model_obj = pool.get(model_name)
-        if field_name not in model_obj._columns:
+        Model = pool.get(model_name)
+        if field_name not in Model._fields:
             return True
-        field = model_obj._columns[field_name]
+        field = Model._fields[field_name]
         return not field.help
 
-    def _clean_error(self, translation):
+    @staticmethod
+    def _clean_error(translation):
         pool = Pool()
         model_name = translation.name
         if model_name in (
@@ -927,30 +944,30 @@
                 ):
             return False
         if model_name in pool.object_name_list():
-            model_obj = pool.get(model_name)
-            errors = model_obj._error_messages.values() + \
-                    model_obj._sql_error_messages.values()
-            for _, _, error in model_obj._sql_constraints:
+            Model = pool.get(model_name)
+            errors = Model._error_messages.values() + \
+                    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'):
-            wizard_obj = pool.get(model_name, type='wizard')
-            errors = wizard_obj._error_messages.values()
+            Wizard = pool.get(model_name, type='wizard')
+            errors = Wizard._error_messages.values()
             if translation.src not in errors:
                 return True
         else:
             return True
 
-    def transition_clean(self, session):
+    def transition_clean(self):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        model_data_obj = pool.get('ir.model.data')
+        Translation = pool.get('ir.translation')
+        ModelData = pool.get('ir.model.data')
 
         to_delete = []
         keys = set()
-        translation_ids = translation_obj.search([])
-        for translation in translation_obj.browse(translation_ids):
+        translations = Translation.search([])
+        for translation in translations:
             if getattr(self, '_clean_%s' % translation.type)(translation):
                 to_delete.append(translation.id)
             elif translation.type in ('field', 'model', 'wizard_button',
@@ -962,46 +979,42 @@
                 else:
                     keys.add(key)
         # skip translation handled in ir.model.data
-        mdata_ids = model_data_obj.search([
+        models_data = ModelData.search([
             ('db_id', 'in', to_delete),
             ('model', '=', 'ir.translation'),
             ])
-        for mdata in model_data_obj.browse(mdata_ids):
+        for mdata in models_data:
             if mdata.db_id in to_delete:
                 to_delete.remove(mdata.db_id)
 
-        translation_obj.delete(to_delete)
+        Translation.delete(Translation.browse(to_delete))
         return 'succeed'
 
-TranslationClean()
-
 
 class TranslationUpdateStart(ModelView):
     "Update translation"
-    _name = 'ir.translation.update.start'
-    _description = __doc__
+    __name__ = 'ir.translation.update.start'
 
     language = fields.Many2One('ir.lang', 'Language', required=True,
         domain=[('translatable', '=', True)])
 
-    def default_language(self):
-        lang_obj = Pool().get('ir.lang')
+    @staticmethod
+    def default_language():
+        Lang = Pool().get('ir.lang')
         code = Transaction().context.get('language', False)
         try:
-            lang_id, = lang_obj.search([
+            lang, = Lang.search([
                     ('code', '=', code),
                     ('translatable', '=', True),
                     ], limit=1)
-            return lang_id
+            return lang.id
         except ValueError:
             return None
 
-TranslationUpdateStart()
-
 
 class TranslationUpdate(Wizard):
     "Update translation"
-    _name = "ir.translation.update"
+    __name__ = "ir.translation.update"
 
     start = StateView('ir.translation.update.start',
         'ir.translation_update_start_view_form', [
@@ -1010,14 +1023,15 @@
             ])
     update = StateAction('ir.act_translation_form')
 
-    def transition_update(self, session):
+    @staticmethod
+    def transition_update():
         return 'end'
 
-    def do_update(self, session, action):
+    def do_update(self, action):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
         cursor = Transaction().cursor
-        lang = session.start.language.code
+        lang = self.start.language.code
         cursor.execute('SELECT name, res_id, type, src, module ' \
                 'FROM ir_translation ' \
                 'WHERE lang=\'en_US\' ' \
@@ -1031,7 +1045,7 @@
                 (lang,))
         for row in cursor.dictfetchall():
             with Transaction().set_user(0):
-                translation_obj.create({
+                Translation.create({
                     'name': row['name'],
                     'res_id': row['res_id'],
                     'lang': lang,
@@ -1050,7 +1064,7 @@
                 (lang,))
         for row in cursor.dictfetchall():
             with Transaction().set_user(0):
-                translation_obj.create({
+                Translation.create({
                     'name': row['name'],
                     'res_id': row['res_id'],
                     'lang': lang,
@@ -1109,13 +1123,11 @@
         ])
         return action, {}
 
-TranslationUpdate()
-
 
 class TranslationExportStart(ModelView):
     "Export translation"
-    _name = 'ir.translation.export.start'
-    _description = __doc__
+    __name__ = 'ir.translation.export.start'
+
     language = fields.Many2One('ir.lang', 'Language', required=True,
         domain=[
             ('translatable', '=', True),
@@ -1126,33 +1138,30 @@
             ('state', 'in', ['installed', 'to upgrade', 'to remove']),
             ])
 
-    def default_language(self):
-        lang_obj = Pool().get('ir.lang')
+    @staticmethod
+    def default_language():
+        Lang = Pool().get('ir.lang')
         code = Transaction().context.get('language', False)
         try:
-            lang_id, = lang_obj.search([
+            lang, = Lang.search([
                     ('code', '=', code),
                     ('translatable', '=', True),
                     ], limit=1)
-            return lang_id
+            return lang.id
         except ValueError:
             return None
 
-TranslationExportStart()
-
 
 class TranslationExportResult(ModelView):
     "Export translation"
-    _description = __doc__
-    _name = 'ir.translation.export.result'
+    __name__ = 'ir.translation.export.result'
+
     file = fields.Binary('File', readonly=True)
 
-TranslationExportResult()
-
 
 class TranslationExport(Wizard):
     "Export translation"
-    _name = "ir.translation.export"
+    __name__ = "ir.translation.export"
 
     start = StateView('ir.translation.export.start',
         'ir.translation_export_start_view_form', [
@@ -1165,19 +1174,17 @@
             Button('Close', 'end', 'tryton-close'),
             ])
 
-    def transition_export(self, session):
+    def transition_export(self):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        file_data = translation_obj.translation_export(
-            session.start.language.code, session.start.module.name)
-        session.result.file = buffer(file_data)
+        Translation = pool.get('ir.translation')
+        file_data = Translation.translation_export(
+            self.start.language.code, self.start.module.name)
+        self.result.file = buffer(file_data)
         return 'result'
 
-    def default_result(self, session, fields):
-        file_ = session.result.file
-        session.result.file = False  # No need to store it in session
+    def default_result(self, fields):
+        file_ = self.result.file
+        self.result.file = False  # No need to store it in session
         return {
             'file': file_,
             }
-
-TranslationExport()
diff -r fb51f5b4aa4c trytond/ir/trigger.py
--- a/trytond/ir/trigger.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/trigger.py	Mon Sep 24 20:36:36 2012 +0200
@@ -11,11 +11,14 @@
 from ..cache import Cache
 from ..pool import Pool
 
+__all__ = [
+    'Trigger', 'TriggerLog',
+    ]
+
 
 class Trigger(ModelSQL, ModelView):
     "Trigger"
-    _name = 'ir.trigger'
-    _description = __doc__
+    __name__ = 'ir.trigger'
     name = fields.Char('Name', required=True, translate=True)
     active = fields.Boolean('Active', select=True)
     model = fields.Many2One('ir.model', 'Model', required=True, select=True)
@@ -48,41 +51,46 @@
         '0 for no delay.')
     action_model = fields.Many2One('ir.model', 'Action Model', required=True)
     action_function = fields.Char('Action Function', required=True)
+    _get_triggers_cache = Cache('ir_trigger.get_triggers')
 
-    def __init__(self):
-        super(Trigger, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Trigger, cls).__setup__()
+        cls._sql_constraints += [
             ('on_exclusive',
                 'CHECK(NOT(on_time AND (on_create OR on_write OR on_delete)))',
                 '"On Time" and others are mutually exclusive!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_condition', 'invalid_condition'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_condition': 'Condition must be a python expression!',
         })
-        self._order.insert(0, ('name', 'ASC'))
+        cls._order.insert(0, ('name', 'ASC'))
 
-    def check_condition(self, ids):
+    @classmethod
+    def check_condition(cls, triggers):
         '''
         Check condition
         '''
-        for trigger in self.browse(ids):
+        for trigger in triggers:
             try:
                 compile(trigger.condition, '', 'eval')
             except (SyntaxError, TypeError):
                 return False
         return True
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_limit_number(self):
+    @staticmethod
+    def default_limit_number():
         return 0
 
-    def on_change_on_time(self, values):
-        if values.get('on_time'):
+    def on_change_on_time(self):
+        if self.on_time:
             return {
                     'on_create': False,
                     'on_write': False,
@@ -90,35 +98,31 @@
                     }
         return {}
 
-    def on_change_on_create(self, values):
-        if values.get('on_create'):
+    def on_change_on_create(self):
+        if self.on_create:
             return {
                     'on_time': False,
                     }
         return {}
 
-    def on_change_on_write(self, values):
-        if values.get('on_write'):
+    def on_change_on_write(self):
+        if self.on_write:
             return {
                     'on_time': False,
                     }
         return {}
 
-    def on_change_on_delete(self, values):
-        if values.get('on_delete'):
+    def on_change_on_delete(self):
+        if self.on_delete:
             return {
                     'on_time': False,
                     }
         return {}
 
-    @Cache('ir_trigger.get_triggers')
-    def get_triggers(self, model_name, mode):
+    @classmethod
+    def get_triggers(cls, model_name, mode):
         """
-        Return trigger ids for a model and a mode
-
-        :param model_name: the name of the model
-        :param mode: the mode that can be 'create', 'write', 'delete' or 'time'
-        :return: a list of ir.trigger ids
+        Return triggers for a model and a mode
         """
         assert mode in ['create', 'write', 'delete', 'time'], \
                 'Invalid trigger mode'
@@ -126,19 +130,22 @@
         if Transaction().user == 0:
             return []  # XXX is it want we want?
 
-        trigger_ids = self.search([
-            ('model.model', '=', model_name),
-            ('on_%s' % mode, '=', True),
-            ])
-        return trigger_ids
+        key = (model_name, mode)
+        trigger_ids = cls._get_triggers_cache.get(key)
+        if trigger_ids is not None:
+            return cls.browse(trigger_ids)
 
-    def eval(self, trigger, record):
+        triggers = cls.search([
+                ('model.model', '=', model_name),
+                ('on_%s' % mode, '=', True),
+                ])
+        cls._get_triggers_cache.set(key, map(int, triggers))
+        return triggers
+
+    @staticmethod
+    def eval(trigger, record):
         """
         Evaluate the condition of trigger
-
-        :param trigger: a BrowseRecord of ir.trigger
-        :param record: a BrowseRecord of the tested model
-        :return: a boolean
         """
         env = {}
         env['current_date'] = datetime.datetime.today()
@@ -147,18 +154,16 @@
         env['self'] = record
         return bool(safe_eval(trigger.condition, env))
 
-    def trigger_action(self, ids, trigger_id):
+    @classmethod
+    def trigger_action(cls, records, trigger):
         """
-        Trigger the action define on trigger_id for the ids
-
-        :param ids: the list of record ids triggered
-        :param trigger_id: the trigger id
+        Trigger the action define on trigger for the records
         """
         pool = Pool()
-        trigger_log_obj = pool.get('ir.trigger.log')
-        trigger = self.browse(trigger_id)
-        model_obj = pool.get(trigger.action_model.model)
+        TriggerLog = pool.get('ir.trigger.log')
+        Model = pool.get(trigger.action_model.model)
         cursor = Transaction().cursor
+        ids = map(int, records)
 
         # Filter on limit_number
         if trigger.limit_number:
@@ -169,7 +174,7 @@
                 cursor.execute('SELECT "record_id", COUNT(1) FROM "%s" '
                         'WHERE %s AND "trigger" = %%s '
                         'GROUP BY "record_id"'
-                        % (trigger_log_obj._table, red_sql),
+                        % (TriggerLog._table, red_sql),
                         red_ids + [trigger.id])
                 number = dict(cursor.fetchall())
                 for record_id in sub_ids:
@@ -190,7 +195,7 @@
                         'FROM "%s" '
                         'WHERE %s AND "trigger" = %%s '
                         'GROUP BY "record_id"'
-                        % (trigger_log_obj._table, red_sql),
+                        % (TriggerLog._table, red_sql),
                         red_ids + [trigger.id])
                 delay = dict(cursor.fetchall())
                 for record_id in sub_ids:
@@ -214,66 +219,66 @@
                         new_ids.append(record_id)
             ids = new_ids
 
-        if ids:
-            getattr(model_obj, trigger.action_function)(ids, trigger_id)
+        records = Model.browse(ids)
+        if records:
+            getattr(Model, trigger.action_function)(records, trigger)
         if trigger.limit_number or trigger.minimum_delay:
-            for record_id in ids:
-                trigger_log_obj.create({
+            for record in records:
+                TriggerLog.create({
                     'trigger': trigger.id,
-                    'record_id': record_id,
+                    'record_id': record.id,
                     })
 
-    def trigger_time(self):
+    @classmethod
+    def trigger_time(cls):
         '''
         Trigger time actions
         '''
         pool = Pool()
-        trigger_ids = self.search([
-            ('on_time', '=', True),
-            ])
-        for trigger in self.browse(trigger_ids):
-            model_obj = pool.get(trigger.model.model)
-            triggered_ids = []
+        triggers = cls.search([
+                ('on_time', '=', True),
+                ])
+        for trigger in triggers:
+            Model = pool.get(trigger.model.model)
+            triggered = []
             # TODO add a domain
-            record_ids = model_obj.search([])
-            for record in model_obj.browse(record_ids):
-                if self.eval(trigger, record):
-                    triggered_ids.append(record.id)
-            if triggered_ids:
-                self.trigger_action(triggered_ids, trigger.id)
+            records = Model.search([])
+            for record in records:
+                if cls.eval(trigger, record):
+                    triggered.append(record)
+            if triggered:
+                cls.trigger_action(triggered, trigger)
 
-    def create(self, values):
-        res = super(Trigger, self).create(values)
+    @classmethod
+    def create(cls, values):
+        res = super(Trigger, cls).create(values)
         # Restart the cache on the get_triggers method of ir.trigger
-        self.get_triggers.reset()
+        cls._get_triggers_cache.clear()
         return res
 
-    def write(self, ids, values):
-        res = super(Trigger, self).write(ids, values)
+    @classmethod
+    def write(cls, ids, values):
+        res = super(Trigger, cls).write(ids, values)
         # Restart the cache on the get_triggers method of ir.trigger
-        self.get_triggers.reset()
+        cls._get_triggers_cache.clear()
         return res
 
-    def delete(self, ids):
-        res = super(Trigger, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        super(Trigger, cls).delete(records)
         # Restart the cache on the get_triggers method of ir.trigger
-        self.get_triggers.reset()
-        return res
-
-Trigger()
+        cls._get_triggers_cache.clear()
 
 
 class TriggerLog(ModelSQL):
     'Trigger Log'
-    _name = 'ir.trigger.log'
-    _description = __doc__
+    __name__ = 'ir.trigger.log'
     trigger = fields.Many2One('ir.trigger', 'Trigger', required=True)
     record_id = fields.Integer('Record ID', required=True)
 
-    def init(self, module_name):
-        super(TriggerLog, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(TriggerLog, cls).__register__(module_name)
 
-        table = TableHandler(Transaction().cursor, self, module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action(['trigger', 'record_id'], 'add')
-
-TriggerLog()
diff -r fb51f5b4aa4c trytond/ir/ui/__init__.py
--- a/trytond/ir/ui/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/ui/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,5 +1,2 @@
 #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 .menu import *
-from .view import *
-from .icon import *
diff -r fb51f5b4aa4c trytond/ir/ui/icon.py
--- a/trytond/ir/ui/icon.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/ui/icon.py	Mon Sep 24 20:36:36 2012 +0200
@@ -6,12 +6,16 @@
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.tools import file_open
 from trytond.transaction import Transaction
+from trytond.rpc import RPC
+
+__all__ = [
+    'Icon',
+    ]
 
 
 class Icon(ModelSQL, ModelView):
     'Icon'
-    _name = 'ir.ui.icon'
-    _description = __doc__
+    __name__ = 'ir.ui.icon'
 
     name = fields.Char('Name', required=True, select=True)
     module = fields.Char('Module', readonly=True, required=True)
@@ -19,33 +23,32 @@
     icon = fields.Function(fields.Char('Icon', depends=['path']), 'get_icon')
     sequence = fields.Integer('Sequence', required=True)
 
-    def __init__(self):
-        super(Icon, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._rpc.update({
-            'list_icons': False,
-        })
+    @classmethod
+    def __setup__(cls):
+        super(Icon, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls.__rpc__.update({
+                'list_icons': RPC(),
+                })
 
-    def default_module(self):
+    @staticmethod
+    def default_module():
         return Transaction().context.get('module') or ''
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return 10
 
-    def list_icons(self):
+    @classmethod
+    def list_icons(cls):
         icons = {}
-        for icon in self.browse(self.search([],
+        for icon in cls.browse(cls.search([],
                 order=[('sequence', 'ASC'), ('id', 'ASC')])):
             if icon.name not in icons:
                 icons[icon.name] = icon.id
         return sorted((icon_id, name) for name, icon_id in icons.iteritems())
 
-    def get_icon(self, ids, name):
-        result = {}
-        for icon in self.browse(ids):
-            path = os.path.join(icon.module, icon.path.replace('/', os.sep))
-            with file_open(path, subdir='modules') as fp:
-                result[icon.id] = fp.read()
-        return result
-
-Icon()
+    def get_icon(self, name):
+        path = os.path.join(self.module, self.path.replace('/', os.sep))
+        with file_open(path, subdir='modules') as fp:
+            return fp.read()
diff -r fb51f5b4aa4c trytond/ir/ui/menu.py
--- a/trytond/ir/ui/menu.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/ui/menu.py	Mon Sep 24 20:36:36 2012 +0200
@@ -4,6 +4,10 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = [
+    'UIMenu',
+    ]
+
 
 def one_in(i, j):
     """Check the presence of an element of setA in setB
@@ -64,8 +68,8 @@
 
 class UIMenu(ModelSQL, ModelView):
     "UI menu"
-    _name = 'ir.ui.menu'
-    _description = __doc__
+    __name__ = 'ir.ui.menu'
+
     name = fields.Char('Menu', required=True, translate=True)
     sequence = fields.Integer('Sequence', required=True)
     childs = fields.One2Many('ir.ui.menu', 'parent', 'Children')
@@ -86,56 +90,51 @@
                 ]), 'get_action', setter='set_action')
     active = fields.Boolean('Active')
 
-    def __init__(self):
-        super(UIMenu, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(UIMenu, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._constraints += [
             ('check_recursion', 'recursive_menu'),
             ('check_name', 'wrong_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_menu': 'You can not create recursive menu!',
             'wrong_name': 'You can not use "%s" in name field!' % SEPARATOR,
         })
 
-    def default_icon(self):
+    @staticmethod
+    def default_icon():
         return 'tryton-open'
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return 10
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def list_icons(self):
+    @staticmethod
+    def list_icons():
         pool = Pool()
-        icon_obj = pool.get('ir.ui.icon')
+        Icon = pool.get('ir.ui.icon')
         return sorted(CLIENT_ICONS
-            + [(name, name) for _, name in icon_obj.list_icons()])
+            + [(name, name) for _, name in Icon.list_icons()])
 
-    def check_name(self, ids):
-        for menu in self.browse(ids):
-            if SEPARATOR in menu.name:
-                return False
+    def check_name(self):
+        if SEPARATOR in self.name:
+            return False
         return True
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.get_rec_name(name) + SEPARATOR + self.name
+        else:
+            return self.name
 
-        def _name(menu):
-            if menu.id in res:
-                return res[menu.id]
-            elif menu.parent:
-                return _name(menu.parent) + SEPARATOR + menu.name
-            else:
-                return menu.name
-        for menu in self.browse(ids):
-            res[menu.id] = _name(menu)
-        return res
-
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         if isinstance(clause[2], basestring):
             values = clause[2].split(SEPARATOR)
             values.reverse()
@@ -144,85 +143,81 @@
             for name in values:
                 domain.append((field, clause[1], name))
                 field = 'parent.' + field
-            ids = self.search(domain, order=[])
+            ids = cls.search(domain, order=[])
             return [('id', 'in', ids)]
         #TODO Handle list
         return [('name',) + tuple(clause[1:])]
 
-    def search(self, domain, offset=0, limit=None, order=None, count=False,
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False,
             query_string=False):
-        res = super(UIMenu, self).search(domain, offset=offset, limit=limit,
+        menus = super(UIMenu, cls).search(domain, offset=offset, limit=limit,
                 order=order, count=False, query_string=query_string)
         if query_string:
-            return res
+            return menus
 
-        if res:
-            menus = self.browse(res)
+        if menus:
             parent_ids = [x.parent.id for x in menus if x.parent]
-            parent_ids = self.search([
-                ('id', 'in', parent_ids),
-                ])
-            res = [x.id for x in menus
-                    if (x.parent.id in parent_ids) or not x.parent]
+            parents = cls.search([
+                    ('id', 'in', parent_ids),
+                    ])
+            menus = [x for x in menus
+                if (x.parent and x.parent in parents) or not x.parent]
 
         if count:
-            return len(res)
-        return res
+            return len(menus)
+        return menus
 
-    def get_action(self, ids, name):
+    @classmethod
+    def get_action(cls, menus, name):
         pool = Pool()
-        action_keyword_obj = pool.get('ir.action.keyword')
-        res = {}
-        for menu_id in ids:
-            res[menu_id] = None
+        ActionKeyword = pool.get('ir.action.keyword')
+        actions = dict((m.id, None) for m in menus)
         with Transaction().set_context(active_test=False):
-            action_keyword_ids = action_keyword_obj.search([
-                ('keyword', '=', 'tree_open'),
-                ('model', 'in', [self._name + ',' + str(x) for x in ids]),
-                ])
-        for action_keyword in action_keyword_obj.browse(action_keyword_ids):
-            model_id = int(
-                action_keyword.model.split(',')[1].split(',')[0].strip('('))
-            action_obj = pool.get(action_keyword.action.type)
+            action_keywords = ActionKeyword.search([
+                    ('keyword', '=', 'tree_open'),
+                    ('model', 'in', [str(m) for m in menus]),
+                    ])
+        for action_keyword in action_keywords:
+            model = action_keyword.model
+            Action = pool.get(action_keyword.action.type)
             with Transaction().set_context(active_test=False):
-                action_id = action_obj.search([
-                    ('action', '=', action_keyword.action.id),
-                    ])
-            if action_id:
-                action_id = action_id[0]
+                factions = Action.search([
+                        ('action', '=', action_keyword.action.id),
+                        ], limit=1)
+            if factions:
+                action, = factions
             else:
-                action_id = 0
-            res[model_id] = action_keyword.action.type + ',' + str(action_id)
-        return res
+                action = '%s,0' % action_keyword.action.type
+            actions[model.id] = str(action)
+        return actions
 
-    def set_action(self, ids, name, value):
+    @classmethod
+    def set_action(cls, menus, name, value):
         if not value:
             return
         pool = Pool()
-        action_keyword_obj = pool.get('ir.action.keyword')
-        action_keyword_ids = []
+        ActionKeyword = pool.get('ir.action.keyword')
+        action_keywords = []
         cursor = Transaction().cursor
-        for i in range(0, len(ids), cursor.IN_MAX):
-            sub_ids = ids[i:i + cursor.IN_MAX]
-            action_keyword_ids += action_keyword_obj.search([
+        for i in range(0, len(menus), cursor.IN_MAX):
+            sub_menus = menus[i:i + cursor.IN_MAX]
+            action_keywords += ActionKeyword.search([
                 ('keyword', '=', 'tree_open'),
-                ('model', 'in', [self._name + ',' + str(menu_id)
-                    for menu_id in sub_ids]),
+                ('model', 'in', [str(menu) for menu in sub_menus]),
                 ])
-        if action_keyword_ids:
+        if action_keywords:
             with Transaction().set_context(_timestamp=False):
-                action_keyword_obj.delete(action_keyword_ids)
+                ActionKeyword.delete(action_keywords)
         action_type, action_id = value.split(',')
         if not int(action_id):
             return
-        action_obj = pool.get(action_type)
-        action = action_obj.browse(int(action_id))
-        for menu_id in ids:
+        Action = pool.get(action_type)
+        action = Action(int(action_id))
+        for menu in menus:
             with Transaction().set_context(_timestamp=False):
-                action_keyword_obj.create({
+                ActionKeyword.create({
                     'keyword': 'tree_open',
-                    'model': self._name + ',' + str(menu_id),
+                    'model': str(menu),
                     'action': action.action.id,
                     })
-
-UIMenu()
diff -r fb51f5b4aa4c trytond/ir/ui/view.py
--- a/trytond/ir/ui/view.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/ir/ui/view.py	Mon Sep 24 20:36:36 2012 +0200
@@ -9,15 +9,19 @@
 from trytond.pyson import CONTEXT, Eval
 from trytond.tools import safe_eval
 from trytond.transaction import Transaction
-from trytond.wizard import Wizard, StateView, StateAction, StateTransition, \
-    Button
+from trytond.wizard import Wizard, StateView, StateAction, Button
 from trytond.pool import Pool
+from trytond.rpc import RPC
+
+__all__ = [
+    'View', 'ShowViewStart', 'ShowView', 'ViewShortcut', 'ShowView',
+    'OpenShortcut', 'ViewTreeWidth', 'ViewTreeExpandedState',
+    ]
 
 
 class View(ModelSQL, ModelView):
     "View"
-    _name = 'ir.ui.view'
-    _description = __doc__
+    __name__ = 'ir.ui.view'
     _rec_name = 'model'
     model = fields.Char('Model', select=True, states={
             'required': Eval('type').in_([None, 'tree', 'form', 'graph']),
@@ -41,22 +45,24 @@
             'invisible': ~Eval('inherit'),
             }, depends=['inherit'])
 
-    def __init__(self):
-        super(View, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(View, cls).__setup__()
+        cls._constraints += [
             ('check_xml', 'invalid_xml'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_xml': 'Invalid XML for View!',
         })
-        self._order.insert(0, ('priority', 'ASC'))
-        self._buttons.update({
+        cls._order.insert(0, ('priority', 'ASC'))
+        cls._buttons.update({
                 'show': {},
                 })
 
-    def init(self, module_name):
-        super(View, self).init(module_name)
-        table = TableHandler(Transaction().cursor, self, module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(View, cls).__register__(module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
 
         # Migration from 1.0 arch no more required
         table.not_null_action('arch', action='remove')
@@ -64,25 +70,29 @@
         # Migration from 2.4 model no more required
         table.not_null_action('model', action='remove')
 
-    def default_arch(self):
+    @staticmethod
+    def default_arch():
         return '<?xml version="1.0"?>'
 
-    def default_priority(self):
+    @staticmethod
+    def default_priority():
         return 16
 
-    def default_module(self):
+    @staticmethod
+    def default_module():
         return Transaction().context.get('module') or ''
 
+    @classmethod
     @ModelView.button_action('ir.act_view_show')
-    def show(self, ids):
+    def show(cls, views):
         pass
 
-    def check_xml(self, ids):
+    @classmethod
+    def check_xml(cls, views):
         "Check XML"
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
         cursor = Transaction().cursor
-        views = self.browse(ids)
         for view in views:
             cursor.execute('SELECT id, name, src FROM ir_translation ' \
                     'WHERE lang = %s ' \
@@ -144,20 +154,20 @@
             if not encode(root_element):
                 return False
 
-            strings = self._translate_view(root_element)
+            strings = cls._translate_view(root_element)
             with Transaction().set_user(0):
-                view_ids = self.search([
+                views2 = cls.search([
                     ('model', '=', view.model),
                     ('id', '!=', view.id),
                     ('module', '=', view.module),
                     ])
-                for view2 in self.browse(view_ids):
+                for view2 in views2:
                     xml2 = view2.arch.strip()
                     if not xml2:
                         continue
                     tree2 = etree.fromstring(xml2)
                     root2_element = tree2.getroottree().getroot()
-                    strings += self._translate_view(root2_element)
+                    strings += cls._translate_view(root2_element)
             if not strings:
                 continue
             for string in set(strings):
@@ -165,7 +175,7 @@
                 if string in trans_views:
                     del trans_views[string]
                     continue
-                string_md5 = translation_obj.get_src_md5(string)
+                string_md5 = Translation.get_src_md5(string)
                 for string_trans in trans_views:
                     if string_trans in strings:
                         continue
@@ -203,40 +213,27 @@
                         (view.model, 'view', view.module) + tuple(strings))
         return True
 
-    def delete(self, ids):
-        res = super(View, self).delete(ids)
+    @classmethod
+    def delete(cls, views):
+        super(View, cls).delete(views)
         # Restart the cache
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
+        ModelView._fields_view_get_cache.clear()
 
-    def create(self, vals):
-        res = super(View, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        view = super(View, cls).create(vals)
         # Restart the cache
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
+        ModelView._fields_view_get_cache.clear()
+        return view
 
-    def write(self, ids, vals):
-        res = super(View, self).write(ids, vals)
+    @classmethod
+    def write(cls, views, vals):
+        super(View, cls).write(views, vals)
         # Restart the cache
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
+        ModelView._fields_view_get_cache.clear()
 
-    def _translate_view(self, element):
+    @classmethod
+    def _translate_view(cls, element):
         strings = []
         for attr in ('string', 'sum', 'confirm', 'help'):
             if element.get(attr):
@@ -244,22 +241,18 @@
                 if string:
                     strings.append(string)
         for child in element:
-            strings.extend(self._translate_view(child))
+            strings.extend(cls._translate_view(child))
         return strings
 
-View()
-
 
 class ShowViewStart(ModelView):
     'Show view'
-    _name = 'ir.ui.view.show.start'
-
-ShowViewStart()
+    __name__ = 'ir.ui.view.show.start'
 
 
 class ShowView(Wizard):
     'Show view'
-    _name = 'ir.ui.view.show'
+    __name__ = 'ir.ui.view.show'
 
     class ShowStateView(StateView):
 
@@ -268,28 +261,25 @@
 
         def get_view(self):
             pool = Pool()
-            view_obj = pool.get('ir.ui.view')
-            view_id = Transaction().context.get('active_id')
-            view = view_obj.browse(view_id)
-            model_obj = pool.get(view.model)
+            View = pool.get('ir.ui.view')
+            view = View(Transaction().context.get('active_id'))
+            Model = pool.get(view.model)
             if view.type != 'form':
-                return model_obj.fields_view_get(view_type='form')
-            return model_obj.fields_view_get(view_id=view_id)
+                return Model.fields_view_get(view_type='form')
+            return Model.fields_view_get(view_id=view.id)
 
-        def get_defaults(self, wizard, session, state_name, fields):
+        def get_defaults(self, wizard, state_name, fields):
             return {}
 
     start = ShowStateView('ir.ui.view.show.start', [
             Button('Close', 'end', 'tryton-close', default=True),
             ])
 
-ShowView()
-
 
 class ViewShortcut(ModelSQL, ModelView):
     "View shortcut"
-    _name = 'ir.ui.view_sc'
-    _description = __doc__
+    __name__ = 'ir.ui.view_sc'
+
     name = fields.Char('Shortcut Name', required=True)
     res_id = fields.Integer('Resource Ref.', required=True)
     sequence = fields.Integer('Sequence', required=True)
@@ -297,34 +287,37 @@
        ondelete='CASCADE')
     resource = fields.Char('Resource Name', required=True)
 
-    def __init__(self):
-        super(ViewShortcut, self).__init__()
-        self._rpc.update({'get_sc': False})
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ViewShortcut, cls).__setup__()
+        cls.__rpc__.update({
+                'get_sc': RPC(),
+                })
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def get_sc(self, user_id, model='ir.ui.menu'):
+    @classmethod
+    def get_sc(cls, user_id, model='ir.ui.menu'):
         "Provide user's shortcuts"
         result = []
-        ids = self.search([
+        shortcuts = cls.search([
                 ('user_id', '=', user_id),
                 ('resource', '=', model),
                 ])
-        for shorcut in self.browse(ids):
+        for shorcut in shortcuts:
             result.append({
                     'res_id': shorcut.res_id,
                     'name': shorcut.name,
                     })
         return result
 
-    def default_resource(self):
+    @staticmethod
+    def default_resource():
         return 'ir.ui.menu'
 
-ViewShortcut()
-
 
 class OpenShortcut(Wizard):
     'Open a shortcut'
-    _name = 'ir.ui.view_sc.open'
+    __name__ = 'ir.ui.view_sc.open'
 
     start_state = 'open_'
 
@@ -337,41 +330,40 @@
 
     open_ = OpenStateAction()
 
-    def transition_open_(self, session):
+    @staticmethod
+    def transition_open_():
         return 'end'
 
-    def do_open_(self, session, action):
+    @staticmethod
+    def do_open_(action):
         pool = Pool()
-        view_sc_obj = pool.get('ir.ui.view_sc')
-        action_keyword_obj = pool.get('ir.action.keyword')
-        action_obj = pool.get('ir.action')
+        ViewSC = pool.get('ir.ui.view_sc')
+        ActionKeyword = pool.get('ir.action.keyword')
+        Action = pool.get('ir.action')
 
-        view_sc = view_sc_obj.browse(Transaction().context.get('active_id'))
+        view_sc = ViewSC(Transaction().context.get('active_id'))
         models = (
                 '%s,%d' % (view_sc.resource, view_sc.res_id),
                 '%s,0' % (view_sc.resource),
                 )
-        action_keyword_ids = None
+        action_keywords = None
         for model in models:
-            action_keyword_ids = action_keyword_obj.search([
-                ('keyword', '=', 'tree_open'),
-                ('model', '=', model),
-                ])
-            if action_keyword_ids:
+            action_keywords = ActionKeyword.search([
+                    ('keyword', '=', 'tree_open'),
+                    ('model', '=', model),
+                    ])
+            if action_keywords:
                 break
-        if not action_keyword_ids:
+        if not action_keywords:
             return {}, {}
-        action_keyword = action_keyword_obj.browse(action_keyword_ids[0])
-        return action_obj.get_action_values(action_keyword.action.type,
-            action_keyword.action.id), {}
-
-OpenShortcut()
+        action_keyword = action_keywords[0]
+        return Action.get_action_values(action_keyword.action.type,
+            [action_keyword.action.id])[0], {}
 
 
 class ViewTreeWidth(ModelSQL, ModelView):
     "View Tree Width"
-    _name = 'ir.ui.view_tree_width'
-    _description = __doc__
+    __name__ = 'ir.ui.view_tree_width'
     _rec_name = 'model'
     model = fields.Char('Model', required=True, select=True)
     field = fields.Char('Field', required=True, select=True)
@@ -379,85 +371,54 @@
             ondelete='CASCADE', select=True)
     width = fields.Integer('Width', required=True)
 
-    def __init__(self):
-        super(ViewTreeWidth, self).__init__()
-        self._rpc.update({
-            'set_width': True,
-        })
+    @classmethod
+    def __setup__(cls):
+        super(ViewTreeWidth, cls).__setup__()
+        cls.__rpc__.update({
+                'set_width': RPC(readonly=False),
+                })
 
-    def delete(self, ids):
-        pool = Pool()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        views = self.browse(ids)
-        for view in views:
-            # Restart the cache
-            try:
-                pool.get(view.model).fields_view_get.reset()
-            except Exception:
-                pass
-        res = super(ViewTreeWidth, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        ModelView._fields_view_get_cache.clear()
+        super(ViewTreeWidth, cls).delete(records)
+
+    @classmethod
+    def create(cls, vals):
+        res = super(ViewTreeWidth, cls).create(vals)
+        ModelView._fields_view_get_cache.clear()
         return res
 
-    def create(self, vals):
-        pool = Pool()
-        res = super(ViewTreeWidth, self).create(vals)
-        if 'model' in vals:
-            model = vals['model']
-            # Restart the cache
-            try:
-                pool.get(model).fields_view_get.reset()
-            except Exception:
-                pass
-        return res
+    @classmethod
+    def write(cls, records, vals):
+        super(ViewTreeWidth, cls).write(records, vals)
+        ModelView._fields_view_get_cache.clear()
 
-    def write(self, ids, vals):
-        pool = Pool()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        views = self.browse(ids)
-        for view in views:
-            # Restart the cache
-            try:
-                pool.get(view.model).fields_view_get.reset()
-            except Exception:
-                pass
-        res = super(ViewTreeWidth, self).write(ids, vals)
-        views = self.browse(ids)
-        for view in views:
-            # Restart the cache
-            try:
-                pool.get(view.model).fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-    def set_width(self, model, fields):
+    @classmethod
+    def set_width(cls, model, fields):
         '''
         Set width for the current user on the model.
         fields is a dictionary with key: field name and value: width.
         '''
-        ids = self.search([
+        records = cls.search([
             ('user', '=', Transaction().user),
             ('model', '=', model),
             ('field', 'in', fields.keys()),
             ])
-        self.delete(ids)
+        cls.delete(records)
 
         for field in fields.keys():
-            self.create({
+            cls.create({
                 'model': model,
                 'field': field,
                 'user': Transaction().user,
                 'width': fields[field],
                 })
 
-ViewTreeWidth()
-
 
 class ViewTreeExpandedState(ModelSQL, ModelView):
-    _name = 'ir.ui.view_tree_expanded_state'
-
+    'View Tree Expanded State'
+    __name__ = 'ir.ui.view_tree_expanded_state'
     _rec_name = 'model'
     model = fields.Char('Model', required=True)
     domain = fields.Char('Domain', required=True)
@@ -466,34 +427,38 @@
     child_name = fields.Char('Child Name')
     nodes = fields.Text('Expanded Nodes')
 
-    def __init__(self):
-        super(ViewTreeExpandedState, self).__init__()
-        self._rpc.update({
-                'set_expanded': True,
-                'get_expanded': True,
+    @classmethod
+    def __setup__(cls):
+        super(ViewTreeExpandedState, cls).__setup__()
+        cls.__rpc__.update({
+                'set_expanded': RPC(readonly=False),
+                'get_expanded': RPC(),
                 })
 
-    def init(self, module_name):
-        super(ViewTreeExpandedState, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ViewTreeExpandedState, cls).__register__(module_name)
 
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action(['model', 'domain', 'user', 'child_name'], 'add')
 
-    def default_nodes(self):
+    @staticmethod
+    def default_nodes():
         return '[]'
 
-    def set_expanded(self, model, domain, child_name, nodes):
+    @classmethod
+    def set_expanded(cls, model, domain, child_name, nodes):
         current_user = Transaction().user
         with Transaction().set_user(0):
-            ids = self.search([
+            records = cls.search([
                     ('user', '=', current_user),
                     ('model', '=', model),
                     ('domain', '=', domain),
                     ('child_name', '=', child_name),
                     ])
-            self.delete(ids)
-            self.create({
+            cls.delete(records)
+            cls.create({
                     'user': current_user,
                     'model': model,
                     'domain': domain,
@@ -501,11 +466,12 @@
                     'nodes': nodes,
                     })
 
-    def get_expanded(self, model, domain, child_name):
+    @classmethod
+    def get_expanded(cls, model, domain, child_name):
         current_user = Transaction().user
         with Transaction().set_user(0):
             try:
-                expanded_info, = self.search([
+                expanded_info, = cls.search([
                         ('user', '=', current_user),
                         ('model', '=', model),
                         ('domain', '=', domain),
@@ -514,7 +480,4 @@
                     limit=1)
             except ValueError:
                 return '[]'
-            return self.browse(expanded_info).nodes
-
-
-ViewTreeExpandedState()
+            return cls(expanded_info).nodes
diff -r fb51f5b4aa4c trytond/model/__init__.py
--- a/trytond/model/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,8 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from .model import *
-from .modelview import *
-from .modelstorage import *
-from .modelsingleton import *
-from .modelsql import *
+from .model import Model
+from .modelview import ModelView
+from .modelstorage import ModelStorage
+from .modelsingleton import ModelSingleton
+from .modelsql import ModelSQL
 from .workflow import Workflow
+
+__all__ = ['Model', 'ModelView', 'ModelStorage', 'ModelSingleton', 'ModelSQL',
+    'Workflow']
diff -r fb51f5b4aa4c trytond/model/browse.py
--- a/trytond/model/browse.py	Mon Sep 24 11:36:39 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,335 +0,0 @@
-#This file is part of Tryton.  The COPYRIGHT file at the top level of
-#this repository contains the full copyright notices and license terms.
-import contextlib
-from itertools import islice, ifilter, ifilterfalse, chain
-from trytond.config import CONFIG
-from trytond.model import fields
-from trytond.transaction import Transaction
-from trytond.pool import Pool
-from trytond.cache import LRUDict
-from trytond.const import MODEL_CACHE_SIZE, RECORD_CACHE_SIZE, \
-        BROWSE_FIELD_TRESHOLD
-
-
-class BrowseRecordList(list):
-    '''
-    A list of BrowseRecord
-    '''
-    #TODO: execute an object method on BrowseRecordList
-
-    def __init__(self, lst):
-        super(BrowseRecordList, self).__init__(lst)
-
-    def get_eval(self):
-        return [record.id for record in self]
-
-
-class BrowseRecordNull(object):
-    '''
-    An object that represents an empty record.
-    '''
-
-    def __init__(self):
-        self.id = None
-
-    def __getitem__(self, name):
-        return None
-
-    def __int__(self):
-        return None
-
-    def __str__(self):
-        return None
-
-    def __nonzero__(self):
-        return False
-
-
-class BrowseRecord(object):
-    '''
-    An object that represents record defined by a ORM object.
-    '''
-
-    def __init__(self, record_id, model, ids, local_cache):
-        assert isinstance(ids, list)
-        if ids:
-            assert record_id in ids
-        self._cursor = Transaction().cursor
-        self._user = Transaction().user
-        self.id = record_id
-        self._model = model
-        self._model_name = self._model._name
-        self._context = Transaction().context
-        self._pool = Pool()
-        self._ids = ids
-
-        cache = self._cursor.get_cache(self._context)
-        if model._name not in cache:
-            cache[model._name] = LRUDict(RECORD_CACHE_SIZE)
-        self._data = cache[model._name]
-        self._cache = cache
-        assert isinstance(local_cache, LRUDict)
-        self._local_data = local_cache
-
-    def __getitem__(self, name):
-        # Access to LRUDict must be atomic
-        result = self._local_data.get(self.id, {}).get(name)
-        if (self.id in self._local_data
-                and name in self._local_data[self.id]):
-            return result
-        result = self._data.get(self.id, {}).get(name)
-        if self.id in self._data and name in self._data[self.id]:
-            return result
-
-        # build the list of fields we will fetch
-        # fetch the definition of the field which was asked for
-        if name in self._model._columns:
-            col = self._model._columns[name]
-        elif name in self._model._inherit_fields:
-            col = self._model._inherit_fields[name][2]
-        elif hasattr(self._model, name):
-            return getattr(self._model, name)
-        else:
-            raise Exception('Error', 'Programming error: field "%s" ' \
-                    'does not exist in model "%s"!' \
-                    % (name, self._model._name))
-
-        ffields = {}
-        if col.loading == 'eager':
-            field_access_obj = self._pool.get('ir.model.field.access')
-            fread_accesses = {}
-            for inherit_name in self._model._inherits:
-                inherit_obj = self._pool.get(inherit_name)
-                fread_accesses.update(field_access_obj.check(inherit_name,
-                    inherit_obj._columns.keys(), 'read', access=True))
-            fread_accesses.update(field_access_obj.check(self._model._name,
-                self._model._columns.keys(), 'read', access=True))
-            to_remove = set(x for x, y in fread_accesses.iteritems()
-                    if not y and x != name)
-
-            threshold = BROWSE_FIELD_TRESHOLD
-            inherit_threshold = threshold - len(self._model._columns)
-
-            def not_cached(item):
-                fname, field = item
-                return (fname not in self._data.get(self.id, {})
-                    and fname not in self._local_data.get(self.id, {}))
-
-            def to_load(item):
-                fname, field = item
-                return (field.loading == 'eager'
-                    and fname not in to_remove)
-
-            def overrided(item):
-                fname, field = item
-                return fname in self._model._columns
-
-            if inherit_threshold > 0:
-                ifields = ((fname, field)
-                    for fname, (_, _, field) in
-                    self._model._inherit_fields.iteritems())
-                ifields = ifilterfalse(overrided,
-                    ifilter(to_load,
-                        ifilter(not_cached, ifields)))
-                ifields = islice(ifields, 0, inherit_threshold)
-                ffields.update(ifields)
-                threshold -= inherit_threshold
-
-            ifields = ifilter(to_load,
-                ifilter(not_cached,
-                    self._model._columns.iteritems()))
-            ifields = islice(ifields, 0, threshold)
-            ffields.update(ifields)
-
-        ffields[name] = col
-
-        # add datetime_field
-        for field in ffields.values():
-            if hasattr(field, 'datetime_field') and field.datetime_field:
-                if field.datetime_field in self._model._columns:
-                    datetime_field = self._model._columns[field.datetime_field]
-                else:
-                    datetime_field = self._model._inherit_fields[
-                            field.datetime_field][2]
-                ffields[field.datetime_field] = datetime_field
-
-        def filter_(id_):
-            if (id_ in self._local_data
-                    and name in self._local_data[id_]):
-                return False
-            if id_ in self._data and name in self._data[id_]:
-                return False
-            return True
-        index = self._ids.index(self.id)
-        ids = chain(islice(self._ids, index, None),
-            islice(self._ids, 0, max(index - 1, 0)))
-        ids = list(islice(ifilter(filter_, ids), self._cursor.IN_MAX))
-        model2ids = {}
-        model2cache = {}
-        # read the data
-        with contextlib.nested(Transaction().set_cursor(self._cursor),
-                Transaction().set_user(self._user),
-                Transaction().set_context(self._context)):
-            # create browse records for 'remote' models
-            for data in self._model.read(ids, ffields.keys()):
-                for i, j in ffields.iteritems():
-                    model = None
-                    if (hasattr(j, 'model_name') and
-                            j.model_name in
-                            self._pool.object_name_list()):
-                        model = self._pool.get(j.model_name)
-                    elif hasattr(j, 'get_target'):
-                        model = j.get_target()
-                    if model and j._type in ('many2one', 'one2one'):
-                        if (not data[i]
-                                and not (isinstance(data[i], (int, long))
-                                    and not isinstance(data[i],
-                                        type(False)))):
-                            data[i] = BrowseRecordNull()
-                        else:
-                            _datetime = None
-                            if (hasattr(j, 'datetime_field')
-                                    and j.datetime_field):
-                                _datetime = data[j.datetime_field]
-                            with Transaction().set_context(
-                                    _datetime=_datetime):
-                                ids = model2ids.setdefault(model, [])
-                                ids.append(data[i])
-                                local_cache = model2cache.setdefault(model,
-                                    LRUDict(RECORD_CACHE_SIZE))
-                                data[i] = BrowseRecord(data[i], model,
-                                    ids, local_cache)
-                    elif (model and j._type in ('one2many', 'many2many')):
-                        _datetime = None
-                        if hasattr(j, 'datetime_field') and j.datetime_field:
-                            _datetime = data[j.datetime_field]
-                        with Transaction().set_context(
-                                _datetime=_datetime):
-                            ids = model2ids.setdefault(model, [])
-                            ids.extend(data[i])
-                            local_cache = model2cache.setdefault(model,
-                                LRUDict(RECORD_CACHE_SIZE))
-                            data[i] = BrowseRecordList(
-                                BrowseRecord(x, model, ids, local_cache)
-                                for x in data[i])
-                    if (isinstance(j, fields.Function)
-                            or isinstance(data[i], (BrowseRecord,
-                                    BrowseRecordList))):
-                        if data['id'] == self.id and i == name:
-                            result = data[i]
-                        self._local_data.setdefault(data['id'], {})[i] = \
-                            data[i]
-                        del data[i]
-                self._data.setdefault(data['id'], {}).update(data)
-                if data['id'] == self.id and name in data:
-                    result = data[name]
-        return result
-
-    def __getattr__(self, name):
-        # TODO raise an AttributeError exception
-        return self[name]
-
-    def __contains__(self, name):
-        return (name in self._model._columns) \
-                or (name in self._model._inherit_fields) \
-                or hasattr(self._model, name)
-
-    def __hasattr__(self, name):
-        return name in self
-
-    def __int__(self):
-        return self.id
-
-    def __str__(self):
-        return "BrowseRecord(%s, %d)" % (self._model_name, self.id)
-
-    def __eq__(self, other):
-        if not isinstance(other, BrowseRecord):
-            return False
-        return (self._model_name, self.id) == (other._model_name, other.id)
-
-    def __ne__(self, other):
-        if not isinstance(other, BrowseRecord):
-            return True
-        return (self._model_name, self.id) != (other._model_name, other.id)
-
-    # we need to define __unicode__ even though we've already defined __str__
-    # because we have overridden __getattr__
-    def __unicode__(self):
-        return unicode(str(self))
-
-    def __hash__(self):
-        return hash((self._model_name, self.id))
-
-    def __nonzero__(self):
-        return True
-
-    __repr__ = __str__
-
-    def setLang(self, lang):
-        self._context = self._context.copy()
-        prev_lang = self._context.get('language') or CONFIG['language']
-        self._context['language'] = lang
-        for cache in (self._cache, {self._model_name: self._local_data}):
-            language_cache = cache.setdefault('_language_cache', {})
-            for model in cache:
-                if model == '_language_cache':
-                    continue
-                for record_id in cache[model]:
-                    language_cache.setdefault(prev_lang,
-                        LRUDict(MODEL_CACHE_SIZE)).setdefault(model,
-                            LRUDict(RECORD_CACHE_SIZE))[record_id] = \
-                            cache[model][record_id]
-                    if lang in language_cache \
-                            and model in language_cache[lang] \
-                            and record_id in language_cache[lang][model]:
-                        cache[model][record_id] = \
-                                language_cache[lang][model][record_id]
-                    else:
-                        cache[model][record_id] = {}
-
-    def get_eval(self, name):
-        res = self[name]
-        if isinstance(res, BrowseRecord):
-            res = res.id
-        if isinstance(res, BrowseRecordList):
-            res = res.get_eval()
-        if isinstance(res, BrowseRecordNull):
-            res = False
-        return res
-
-
-class EvalEnvironment(dict):
-
-    def __init__(self, record, model):
-        super(EvalEnvironment, self).__init__()
-        self._record = record
-        self._model = model
-        self._pool = Pool()
-
-    def __getitem__(self, item):
-        if item.startswith('_parent_'):
-            field = item[8:]
-            if field in self._model._columns:
-                model_name = self._model._columns[field].model_name
-            else:
-                model_name = self._model._inherit_fields[field][2].model_name
-            model = self._pool.get(model_name)
-            return EvalEnvironment(self._record[field], model)
-        if item in self._model._columns \
-                or item in self._model._inherit_fields:
-            return self._record.get_eval(item)
-        return super(EvalEnvironment, self).__getitem__(item)
-
-    def __getattr__(self, item):
-        return self.__getitem__(item)
-
-    def get(self, item, default=None):
-        try:
-            return self.__getitem__(item)
-        except Exception:
-            pass
-        return super(EvalEnvironment, self).get(item, default)
-
-    def __nonzero__(self):
-        return bool(self._record)
diff -r fb51f5b4aa4c trytond/model/cacheable.py
--- a/trytond/model/cacheable.py	Mon Sep 24 11:36:39 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-#This file is part of Tryton.  The COPYRIGHT file at the top level of
-#this repository contains the full copyright notices and license terms.
-from threading import Lock
-import time
-from trytond.transaction import Transaction
-from trytond.cache import Cache
-
-
-class Cacheable(object):
-    _name = None
-
-    def __init__(self):
-        super(Cacheable, self).__init__()
-        self._cache = {}
-        self._max_len = 1024
-        self._timeout = 3600
-        self._lock = Lock()
-        self._timestamp = None
-        Cache._cache_instance.append(self)
-
-    def add(self, key, value):
-        dbname = Transaction().cursor.dbname
-        self._lock.acquire()
-        try:
-            self._cache.setdefault(dbname, {})
-
-            lower = None
-            if len(self._cache[dbname]) > self._max_len:
-                mintime = time.time() - self._timeout
-                for key2 in self._cache[dbname].keys():
-                    last_time = self._cache[dbname][key2][1]
-                    if mintime > last_time:
-                        del self._cache[dbname][key2]
-                    else:
-                        if not lower or lower[1] > last_time:
-                            lower = (key2, last_time)
-            if len(self._cache[dbname]) > self._max_len and lower:
-                del self._cache[dbname][lower[0]]
-
-            self._cache[dbname][key] = (value, time.time())
-        finally:
-            self._lock.release()
-
-    def invalidate(self, key):
-        dbname = Transaction().cursor.dbname
-        self._lock.acquire()
-        try:
-            del self._cache[dbname][key]
-        finally:
-            self._lock.release()
-
-    def get(self, key):
-        dbname = Transaction().cursor.dbname
-        try:
-            return self._cache[dbname][key][0]
-        except KeyError:
-            return None
-
-    def clear(self):
-        dbname = Transaction().cursor.dbname
-        self._lock.acquire()
-        try:
-            self._cache.setdefault(dbname, {})
-            self._cache[dbname].clear()
-            Cache.reset(dbname, self._name)
-        finally:
-            self._lock.release()
diff -r fb51f5b4aa4c trytond/model/fields/field.py
--- a/trytond/model/fields/field.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/fields/field.py	Mon Sep 24 20:36:36 2012 +0200
@@ -121,6 +121,7 @@
         assert loading in ('lazy', 'eager'), \
             'loading must be "lazy" or "eager"'
         self.loading = loading
+        self.name = None
 
     def _get_domain(self):
         return self.__domain
@@ -174,3 +175,15 @@
         self.__context = value
 
     context = property(_get_context, _set_context)
+
+    def __get__(self, inst, cls):
+        if inst is None:
+            return self
+        assert self.name is not None
+        return inst.__getattr__(self.name)
+
+    def __set__(self, inst, value):
+        assert self.name is not None
+        if inst._values is None:
+            inst._values = {}
+        inst._values[self.name] = value
diff -r fb51f5b4aa4c trytond/model/fields/function.py
--- a/trytond/model/fields/function.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/fields/function.py	Mon Sep 24 20:36:36 2012 +0200
@@ -39,6 +39,13 @@
         return Function(copy.copy(self._field), self.getter,
                 setter=self.setter, searcher=self.searcher)
 
+    def __deepcopy__(self, memo):
+        return Function(copy.deepcopy(self._field, memo),
+            copy.deepcopy(self.getter, memo),
+            setter=copy.deepcopy(self.setter, memo),
+            searcher=copy.deepcopy(self.searcher, memo),
+            loading=copy.deepcopy(self.loading, memo))
+
     def __getattr__(self, name):
         return getattr(self._field, name)
 
@@ -46,60 +53,52 @@
         return self._field[name]
 
     def __setattr__(self, name, value):
-        if name in ('_field', '_type', 'getter', 'setter', 'searcher'):
-            return object.__setattr__(self, name, value)
-        return setattr(self._field, name, value)
+        if name in ('_field', '_type', 'getter', 'setter', 'searcher', 'name'):
+            object.__setattr__(self, name, value)
+            if name != 'name':
+                return
+        setattr(self._field, name, value)
 
     def search(self, model, name, clause):
         '''
         Call the searcher.
-
-        :param model: The model.
-        :param name: The name of the field.
-        :param clause: The search domain clause. See ModelStorage.search
-        :return: a list of domain clause.
+        Return a list of clauses.
         '''
         if not self.searcher:
             model.raise_user_error('search_function_missing', name)
         return getattr(model, self.searcher)(name, tuple(clause))
 
-    def get(self, ids, model, name, values=None):
+    def get(self, ids, Model, name, values=None):
         '''
         Call the getter.
         If the function has ``names`` in the function definition then
         it will call it with a list of name.
-
-        :param ids: A list of ids.
-        :param model: The model.
-        :param name: The name of the field or a list of name field.
-        :param values:
-        :return: a dictionary with ids as key and values as value or
-            a dictionary with name as key and a dictionary as value if
-            name is a list of field name.
         '''
+        def call(name):
+            method = getattr(Model, self.getter)
+            records = Model.browse(ids)
+            if not hasattr(method, 'im_self') or method.im_self:
+                return method(records, name)
+            else:
+                return dict((r.id, method(r, name)) for r in records)
         if isinstance(name, list):
             names = name
             # Test is the function works with a list of names
-            if 'names' in inspect.getargspec(getattr(model, self.getter))[0]:
-                return getattr(model, self.getter)(ids, names)
-            res = {}
-            for name in names:
-                res[name] = getattr(model, self.getter)(ids, name)
-            return res
+            if 'names' in inspect.getargspec(getattr(Model, self.getter))[0]:
+                return call(names)
+            return dict((name, call(name)) for name in names)
         else:
             # Test is the function works with a list of names
-            if 'names' in inspect.getargspec(getattr(model, self.getter))[0]:
+            if 'names' in inspect.getargspec(getattr(Model, self.getter))[0]:
                 name = [name]
-            return getattr(model, self.getter)(ids, name)
+            return call(name)
 
-    def set(self, ids, model, name, value):
+    def set(self, ids, Model, name, value):
         '''
         Call the setter.
-
-        :param ids: A list of ids.
-        :param model: The model.
-        :param name: The name of the field.
-        :param value: The value to set.
         '''
         if self.setter:
-            getattr(model, self.setter)(ids, name, value)
+            getattr(Model, self.setter)(Model.browse(ids), name, value)
+
+    def __set__(self, inst, value):
+        self._field.__set__(inst, value)
diff -r fb51f5b4aa4c trytond/model/fields/many2many.py
--- a/trytond/model/fields/many2many.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/fields/many2many.py	Mon Sep 24 20:36:36 2012 +0200
@@ -60,12 +60,6 @@
     def get(self, ids, model, name, values=None):
         '''
         Return target records ordered.
-
-        :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 values: a dictionary with the read values
-        :return: a dictionary with ids as key and values as value
         '''
         if values is None:
             values = {}
@@ -80,41 +74,34 @@
         else:
             order = self.order
 
-        relation_obj = Pool().get(self.relation_name)
-        if self.origin in relation_obj._columns:
-            origin_field = relation_obj._columns[self.origin]
+        Relation = Pool().get(self.relation_name)
+        if self.origin in Relation._fields:
+            origin_field = Relation._fields[self.origin]
         else:
-            origin_field = relation_obj._inherit_fields[self.origin][2]
+            origin_field = Relation._inherit_fields[self.origin][2]
 
-        relation_ids = []
+        relations = []
         for i in range(0, len(ids), Transaction().cursor.IN_MAX):
             sub_ids = ids[i:i + Transaction().cursor.IN_MAX]
             if origin_field._type == 'reference':
-                references = ['%s,%s' % (model._name, x) for x in sub_ids]
+                references = ['%s,%s' % (model.__name__, x) for x in sub_ids]
                 clause = [(self.origin, 'in', references)]
             else:
                 clause = [(self.origin, 'in', sub_ids)]
             clause += [(self.target + '.id', '!=', None)]
-            relation_ids.append(relation_obj.search(clause, order=order))
-        relation_ids = list(chain(*relation_ids))
+            relations.append(Relation.search(clause, order=order))
+        relations = list(chain(*relations))
 
-        for relation in relation_obj.browse(relation_ids):
-            if origin_field._type == 'reference':
-                _, origin_id = relation[self.origin].split(',')
-                origin_id = int(origin_id)
-            else:
-                origin_id = relation[self.origin].id
-            res[origin_id].append(relation[self.target].id)
-        return res
+        for relation in relations:
+            origin_id = getattr(relation, self.origin).id
+            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):
         '''
         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 values: A list of tuples:
+        values: A list of tuples:
             (``create``, ``{<field name>: value}``),
             (``write``, ``<ids>``, ``{<field name>: value}``),
             (``delete``, ``<ids>``),
@@ -127,44 +114,44 @@
         pool = Pool()
         if not values:
             return
-        relation_obj = pool.get(self.relation_name)
-        target_obj = self.get_target()
+        Relation = pool.get(self.relation_name)
+        Target = self.get_target()
 
-        if self.origin in relation_obj._columns:
-            origin_field = relation_obj._columns[self.origin]
+        if self.origin in Relation._fields:
+            origin_field = Relation._fields[self.origin]
         else:
-            origin_field = relation_obj._inherit_fields[self.origin][2]
+            origin_field = Relation._inherit_fields[self.origin][2]
 
         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':
                 for record_id in ids:
-                    relation_obj.create({
+                    Relation.create({
                             self.origin: field_value(record_id),
-                            self.target: target_obj.create(act[1]),
+                            self.target: Target.create(act[1]).id,
                             })
             elif act[0] == 'write':
-                target_obj.write(act[1], act[2])
+                Target.write(Target.browse(act[1]), act[2])
             elif act[0] == 'delete':
-                target_obj.delete(act[1])
+                Target.delete(Target.browse(act[1]))
             elif act[0] == 'delete_all':
-                relations = relation_obj.browse(relation_obj.search([
-                            search_clause(ids),
-                            ]))
-                target_obj.delete([r[self.target].id for r in relations
-                        if r[self.target].id])
+                relations = Relation.search([
+                        search_clause(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]]
@@ -172,58 +159,55 @@
                     target_ids = list(act[1])
                 if not target_ids:
                     continue
-                relation_ids = []
+                relations = []
                 for i in range(0, len(target_ids),
                         Transaction().cursor.IN_MAX):
                     sub_ids = target_ids[i:i + Transaction().cursor.IN_MAX]
-                    relation_ids += relation_obj.search([
+                    relations += Relation.search([
                             search_clause(ids),
                             (self.target, 'in', sub_ids),
                             ])
-                relation_obj.delete(relation_ids)
+                Relation.delete(relations)
             elif act[0] == 'add':
-                if isinstance(act[1], (int, long)):
-                    target_ids = [act[1]]
-                else:
-                    target_ids = list(act[1])
+                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]
-                    relation_ids = relation_obj.search([
+                    relations = Relation.search([
                             search_clause(ids),
                             (self.target, 'in', sub_ids),
                             ])
-                    for relation in relation_obj.browse(relation_ids):
-                        existing_ids.append(relation[self.target].id)
+                    for relation in relations:
+                        existing_ids.append(getattr(relation, self.target).id)
                 for new_id in (x for x in target_ids if x not in existing_ids):
                     for record_id in ids:
-                        relation_obj.create({
+                        Relation.create({
                                 self.origin: field_value(record_id),
                                 self.target: new_id,
                                 })
             elif act[0] == 'unlink_all':
-                target_ids = relation_obj.search([
+                targets = Relation.search([
                         search_clause(ids),
                         (self.target + '.id', '!=', None),
                         ])
-                relation_obj.delete(target_ids)
+                Relation.delete(targets)
             elif act[0] == 'set':
                 if not act[1]:
                     target_ids = []
                 else:
                     target_ids = list(act[1])
-                target_ids2 = relation_obj.search([
+                targets2 = Relation.search([
                         search_clause(ids),
                         (self.target + '.id', '!=', None),
                         ])
-                relation_obj.delete(target_ids2)
+                Relation.delete(targets2)
 
                 for new_id in target_ids:
                     for record_id in ids:
-                        relation_obj.create({
+                        Relation.create({
                                 self.origin: field_value(record_id),
                                 self.target: new_id,
                                 })
@@ -231,18 +215,24 @@
                 raise Exception('Bad arguments')
 
     def get_target(self):
-        '''
-        Return the target model.
+        'Return the target model'
+        Relation = Pool().get(self.relation_name)
+        if not self.target:
+            return Relation
+        if self.target in Relation._fields:
+            return Relation._fields[self.target].get_target()
+        else:
+            return Relation._inherit_fields[self.target][2].get_target()
 
-        :return: A Model
-        '''
-        relation_obj = Pool().get(self.relation_name)
-        if not self.target:
-            return relation_obj
-        if self.target in relation_obj._columns:
-            target_obj = Pool().get(
-                    relation_obj._columns[self.target].model_name)
-        else:
-            target_obj = Pool().get(
-                    relation_obj._inherit_fields[self.target][2].model_name)
-        return target_obj
+    def __set__(self, inst, value):
+        Target = self.get_target()
+
+        def instance(data):
+            if isinstance(data, Target):
+                return data
+            elif isinstance(data, dict):
+                return Target(**data)
+            else:
+                return Target(data)
+        value = [instance(x) for x in (value or [])]
+        super(Many2Many, self).__set__(inst, value)
diff -r fb51f5b4aa4c trytond/model/fields/many2one.py
--- a/trytond/model/fields/many2one.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/fields/many2one.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,7 +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 types import NoneType
 
 from trytond.model.fields.field import Field
+from trytond.pool import Pool
 
 
 class Many2One(Field):
@@ -45,6 +47,7 @@
         self.left = left
         self.right = right
         self.datetime_field = datetime_field
+    __init__.__doc__ += Field.__init__.__doc__
 
     def __get_required(self):
         return self.__required
@@ -56,4 +59,15 @@
 
     required = property(__get_required, __set_required)
 
-    __init__.__doc__ += Field.__init__.__doc__
+    def get_target(self):
+        'Return the target Model'
+        return Pool().get(self.model_name)
+
+    def __set__(self, inst, value):
+        Target = self.get_target()
+        if isinstance(value, dict):
+            value = Target(**value)
+        elif isinstance(value, (int, long)):
+            value = Target(value)
+        assert isinstance(value, (Target, NoneType))
+        super(Many2One, self).__set__(inst, value)
diff -r fb51f5b4aa4c trytond/model/fields/one2many.py
--- a/trytond/model/fields/one2many.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/fields/one2many.py	Mon Sep 24 20:36:36 2012 +0200
@@ -76,19 +76,13 @@
     def get(self, ids, model, name, values=None):
         '''
         Return target records ordered.
-
-        :param ids: a list of ids
-        :param model: the model
-        :param name: a string with the name of the field
-        :param values: a dictionary with the read values
-        :return: a dictionary with ids as key and values as value
         '''
         pool = Pool()
-        relation_obj = pool.get(self.model_name)
-        if self.field in relation_obj._columns:
-            field = relation_obj._columns[self.field]
+        Relation = pool.get(self.model_name)
+        if self.field in Relation._fields:
+            field = Relation._fields[self.field]
         else:
-            field = relation_obj._inherit_fields[self.field][2]
+            field = Relation._inherit_fields[self.field][2]
         res = {}
         for i in ids:
             res[i] = []
@@ -96,11 +90,11 @@
         for i in range(0, len(ids), Transaction().cursor.IN_MAX):
             sub_ids = ids[i:i + Transaction().cursor.IN_MAX]
             if field._type == 'reference':
-                references = ['%s,%s' % (model._name, x) for x in sub_ids]
+                references = ['%s,%s' % (model.__name__, x) for x in sub_ids]
                 clause = [(self.field, 'in', references)]
             else:
                 clause = [(self.field, 'in', sub_ids)]
-            ids2.append(relation_obj.search(clause, order=self.order))
+            ids2.append(map(int, Relation.search(clause, order=self.order)))
 
         cache = Transaction().cursor.get_cache(Transaction().context)
         cache.setdefault(self.model_name, {})
@@ -118,7 +112,7 @@
                 ids3.append(i)
 
         if ids3:
-            for i in relation_obj.read(ids3, [self.field]):
+            for i in Relation.read(ids3, [self.field]):
                 if field._type == 'reference':
                     _, id_ = i[self.field].split(',')
                     id_ = int(id_)
@@ -128,18 +122,14 @@
 
         index_of_ids2 = dict((i, index)
             for index, i in enumerate(chain(*ids2)))
-        for val in res.values():
-            val.sort(key=lambda x: index_of_ids2[x])
+        for id_, val in res.iteritems():
+            res[id_] = tuple(sorted(val, key=lambda x: index_of_ids2[x]))
         return res
 
     def set(self, ids, model, name, values):
         '''
         Set the values.
-
-        :param ids: A list of ids
-        :param model: the model
-        :param name: A string with the name of the field
-        :param values: A list of tuples:
+        values: A list of tuples:
             (``create``, ``{<field name>: value}``),
             (``write``, ``<ids>``, ``{<field name>: value}``),
             (``delete``, ``<ids>``),
@@ -149,25 +139,24 @@
             (``unlink_all``),
             (``set``, ``<ids>``)
         '''
-        pool = Pool()
         if not values:
             return
-        target_obj = pool.get(self.model_name)
-        if self.field in target_obj._columns:
-            field = target_obj._columns[self.field]
+        Target = self.get_target()
+        if self.field in Target._fields:
+            field = Target._fields[self.field]
         else:
-            field = target_obj._inherit_fields[self.field][2]
+            field = Target._inherit_fields[self.field][2]
 
         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
 
@@ -175,63 +164,75 @@
             if act[0] == 'create':
                 for record_id in ids:
                     act[1][self.field] = field_value(record_id)
-                    target_obj.create(act[1])
+                    Target.create(act[1])
             elif act[0] == 'write':
-                target_obj.write(act[1], act[2])
+                Target.write(Target.browse(act[1]), act[2])
             elif act[0] == 'delete':
-                target_obj.delete(act[1])
+                Target.delete(Target.browse(act[1]))
             elif act[0] == 'delete_all':
-                target_obj.delete(target_obj.search([
-                            search_clause(ids),
-                            ]))
+                targets = Target.search([
+                        search_clause(ids),
+                        ])
+                Target.delete(targets)
             elif act[0] == 'unlink':
-                if isinstance(act[1], (int, long)):
-                    target_ids = [act[1]]
-                else:
-                    target_ids = list(act[1])
+                target_ids = map(int, act[1])
                 if not target_ids:
                     continue
-                target_ids = target_obj.search([
+                targets = Target.search([
                         search_clause(ids),
                         ('id', 'in', target_ids),
                         ])
-                target_obj.write(target_ids, {
+                Target.write(targets, {
                         self.field: None,
                         })
             elif act[0] == 'add':
-                if isinstance(act[1], (int, long)):
-                    target_ids = [act[1]]
-                else:
-                    target_ids = list(act[1])
+                target_ids = map(int, act[1])
                 if not target_ids:
                     continue
                 for record_id in ids:
-                    target_obj.write(target_ids, {
+                    Target.write(Target.browse(target_ids), {
                             self.field: field_value(record_id),
                             })
             elif act[0] == 'unlink_all':
-                target_ids = target_obj.search([
+                targets = Target.search([
                         search_clause(ids),
                         ])
-                target_obj.write(target_ids, {
+                Target.write(targets, {
                         self.field: None,
                         })
             elif act[0] == 'set':
                 if not act[1]:
-                    target_ids = [0]
+                    target_ids = [-1]
                 else:
-                    target_ids = list(act[1])
+                    target_ids = map(int, act[1])
                 for record_id in ids:
-                    target_ids2 = target_obj.search([
+                    targets = Target.search([
                             search_clause([record_id]),
                             ('id', 'not in', target_ids),
                             ])
-                    target_obj.write(target_ids2, {
+                    Target.write(targets, {
                             self.field: None,
                             })
                     if act[1]:
-                        target_obj.write(target_ids, {
+                        Target.write(Target.browse(target_ids), {
                                 self.field: field_value(record_id),
                                 })
             else:
                 raise Exception('Bad arguments')
+
+    def get_target(self):
+        'Return the target Model'
+        return Pool().get(self.model_name)
+
+    def __set__(self, inst, value):
+        Target = self.get_target()
+
+        def instance(data):
+            if isinstance(data, Target):
+                return data
+            elif isinstance(data, dict):
+                return Target(**data)
+            else:
+                return Target(data)
+        value = [instance(x) for x in (value or [])]
+        super(One2Many, self).__set__(inst, value)
diff -r fb51f5b4aa4c trytond/model/fields/one2one.py
--- a/trytond/model/fields/one2one.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/fields/one2one.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,6 +1,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.
+from types import NoneType
 
+from trytond.model.fields.field import Field
 from trytond.model.fields.many2many import Many2Many
 from trytond.pool import Pool
 
@@ -36,14 +38,23 @@
         :param value: The id to link
         '''
         pool = Pool()
-        relation_obj = pool.get(self.relation_name)
-        relation_ids = relation_obj.search([
-            (self.origin, 'in', ids),
-            ])
-        relation_obj.delete(relation_ids)
+        Relation = pool.get(self.relation_name)
+        relations = Relation.search([
+                (self.origin, 'in', ids),
+                ])
+        Relation.delete(relations)
         if value:
             for record_id in ids:
-                relation_obj.create({
-                    self.origin: record_id,
-                    self.target: value,
-                    })
+                Relation.create({
+                        self.origin: record_id,
+                        self.target: value,
+                        })
+
+    def __set__(self, inst, value):
+        Target = self.get_target()
+        if isinstance(value, dict):
+            value = Target(*value)
+        elif isinstance(value, (int, long)):
+            value = Target(value)
+        assert isinstance(value, (Target, NoneType))
+        Field.__set__(self, inst, value)
diff -r fb51f5b4aa4c trytond/model/fields/property.py
--- a/trytond/model/fields/property.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/fields/property.py	Mon Sep 24 20:36:36 2012 +0200
@@ -25,6 +25,9 @@
     def __copy__(self):
         return Property(copy.copy(self._field))
 
+    def __deepcopy__(self, memo):
+        return Property(copy.deepcopy(self._field))
+
     def get(self, ids, model, name, values=None):
         '''
         Retreive the property.
@@ -36,9 +39,8 @@
         :return: a dictionary with ids as key and values as value
         '''
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        res = property_obj.get(name, model._name, ids)
-        return res
+        Property = pool.get('ir.property')
+        return Property.get(name, model.__name__, ids)
 
     def set(self, ids, model, name, value):
         '''
@@ -50,13 +52,13 @@
         :param value: The value to set.
         '''
         pool = Pool()
-        property_obj = pool.get('ir.property')
+        Property = pool.get('ir.property')
         if value is not None:
             prop_value = '%s,%s' % (getattr(self, 'model_name', ''),
                 str(value))
         else:
             prop_value = None
-        return property_obj.set(name, model._name, ids, prop_value)
+        Property.set(name, model.__name__, ids, prop_value)
 
     def search(self, model, name, clause):
         '''
@@ -65,21 +67,21 @@
         :return: New list of domain.
         '''
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
-        property_obj = pool.get('ir.property')
-        model_obj = pool.get('ir.model')
-        field_obj = pool.get('ir.model.field')
+        Rule = pool.get('ir.rule')
+        Property = pool.get('ir.property')
+        Model = pool.get('ir.model')
+        Field = pool.get('ir.model.field')
         cursor = Transaction().cursor
 
         field_class = backend.FIELDS[self._type]
         if not field_class:
             return []
-        sql_type = field_class.sql_type(model._columns[name])
+        sql_type = field_class.sql_type(model._fields[name])
         if not sql_type:
             return []
         sql_type = sql_type[0]
 
-        property_query, property_val = rule_obj.domain_get('ir.property')
+        property_query, property_val = Rule.domain_get('ir.property')
 
         property_clause = ''
         if property_query:
@@ -88,27 +90,27 @@
         #Fetch res ids that comply with the domain
         cursor.execute(
             'SELECT CAST('
-                'SPLIT_PART("' + property_obj._table + '".res,\',\',2) '
+                'SPLIT_PART("' + Property._table + '".res,\',\',2) '
                     'AS INTEGER), '
-                '"' + property_obj._table + '".id '
-            'FROM "' + property_obj._table + '" '
-                'JOIN "' + field_obj._table + '" ON '
-                    '("' + field_obj._table + '"' +
-                        '.id = "' + property_obj._table + '".field) '
-                'JOIN "' + model_obj._table + '" ON '
-                    '("' + model_obj._table +
-                        '".id = "' + field_obj._table + '".model) '
+                '"' + Property._table + '".id '
+            'FROM "' + Property._table + '" '
+                'JOIN "' + Field._table + '" ON '
+                    '("' + Field._table + '"' +
+                        '.id = "' + Property._table + '".field) '
+                'JOIN "' + Model._table + '" ON '
+                    '("' + Model._table +
+                        '".id = "' + Field._table + '".model) '
             'WHERE '
                 'CASE WHEN "' +
-                    model_obj._table + '".model = %s '
-                    'AND "' + field_obj._table + '".name = %s '
+                    Model._table + '".model = %s '
+                    'AND "' + Field._table + '".name = %s '
                     + property_clause +
                 ' THEN ' +
                     self.get_condition(sql_type, clause) +
                 ' ELSE '
                     '%s '
                 'END',
-            [model._name, name] + property_val +
+            [model.__name__, name] + property_val +
             self.get_condition_args(clause) + [False])
 
         props = cursor.fetchall()
@@ -134,7 +136,7 @@
         #Fetch the res ids that doesn't use the default value
         cursor.execute(
             "SELECT cast(split_part(res,',',2) as integer) "
-            'FROM "' + property_obj._table + '"'
+            'FROM "' + Property._table + '"'
             'WHERE ' + property_query + ' AND res is not null',
             property_val)
 
diff -r fb51f5b4aa4c trytond/model/fields/reference.py
--- a/trytond/model/fields/reference.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/fields/reference.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,6 +1,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 contextlib
+from types import NoneType
+
 from trytond.model.fields.field import Field
 from trytond.transaction import Transaction
 from trytond.pool import Pool
@@ -32,13 +34,7 @@
 
     def get(self, ids, model, name, values=None):
         '''
-        Replace removed reference id by False.
-
-        :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 values: a dictionary with the read values
-        :return: a dictionary with ids as key and values as value
+        Replace removed reference id by None.
         '''
         pool = Pool()
         if values is None:
@@ -71,15 +67,28 @@
         with contextlib.nested(Transaction().set_context(active_test=False),
                 Transaction().set_user(0)):
             for ref_model, (ref_ids, ids) in ref_to_check.iteritems():
-                if ref_model not in pool.object_name_list():
-                    res.update(dict((i, False) for i in ids))
+                try:
+                    pool.get(ref_model)
+                except KeyError:
+                    res.update(dict((i, None) for i in ids))
                     continue
-                ref_obj = pool.get(ref_model)
-                ref_ids = ref_obj.search([
+                Ref = pool.get(ref_model)
+                refs = Ref.search([
                     ('id', 'in', list(ref_ids)),
                     ], order=[])
-                refs = [ref_model + ',' + str(ref_id) for ref_id in ref_ids]
+                refs = map(str, refs)
                 for i in ids:
                     if res[i] not in refs:
-                        res[i] = False
+                        res[i] = None
         return res
+
+    def __set__(self, inst, value):
+        from ..model import Model
+        if not isinstance(value, (Model, NoneType)):
+            if isinstance(value, basestring):
+                target, id_ = value.split(',')
+            else:
+                target, id_ = value
+            Target = Pool().get(target)
+            value = Target(id_)
+        super(Reference, self).__set__(inst, value)
diff -r fb51f5b4aa4c trytond/model/model.py
--- a/trytond/model/model.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/model.py	Mon Sep 24 20:36:36 2012 +0200
@@ -3,154 +3,152 @@
 
 import copy
 import collections
+import warnings
+
 from trytond.model import fields
 from trytond.error import WarningErrorMixin
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.pyson import PYSONEncoder
 from trytond.transaction import Transaction
 from trytond.url import URLMixin
+from trytond.rpc import RPC
+
+__all__ = ['Model']
+
+
+class ModelMeta(PoolMeta):
+
+    def __getattr__(self, name):
+        pool = Pool()
+        for model_name, _ in self._inherits.iteritems():
+            Model = pool.get(model_name)
+            if isinstance(getattr(Model, name, None),
+                    collections.Callable):
+                meth = getattr(Model, name)
+                if not hasattr(meth, 'im_self') or meth.im_self:
+                    # Return classmethod bounded to inherited class
+                    return meth
+                else:
+                    for cls in Model.__mro__:
+                        if name in cls.__dict__:
+                            # Return a method bounded to current class
+                            return cls.__dict__[name].__get__(None, self)
+        raise AttributeError("'%s' Model has no attribute '%s'"
+            % (self.__name__, name))
 
 
 class Model(WarningErrorMixin, URLMixin):
     """
     Define a model in Tryton.
     """
-    _name = None
+    __metaclass__ = ModelMeta
     _inherits = {}
-    _description = ''
-    __columns = None
-    __defaults = None
     _rec_name = 'name'
 
     id = fields.Integer('ID', readonly=True)
 
-    def _reset_columns(self):
-        self.__columns = None
-        self.__defaults = None
+    @classmethod
+    def __setup__(cls):
+        cls.__rpc__ = {
+            'default_get': RPC(),
+            'fields_get': RPC(),
+            'on_change_with': RPC(instantiate=0),
+            }
+        cls._error_messages = {}
 
-    def _getcolumns(self):
-        if self.__columns:
-            return self.__columns
-        res = {}
-        for attr in dir(self):
+        # Copy fields
+        for attr in dir(cls):
             if attr.startswith('_'):
                 continue
-            if isinstance(getattr(self, attr), fields.Field):
-                res[attr] = getattr(self, attr)
-        self.__columns = res
-        return res
+            if isinstance(getattr(cls, attr), fields.Field):
+                setattr(cls, attr, copy.deepcopy(getattr(cls, attr)))
 
-    #replace by _fields
-    _columns = property(fget=_getcolumns)
+    @classmethod
+    def __post_setup__(cls):
+        pool = Pool()
 
-    def _reset_defaults(self):
-        self.__defaults = None
+        # Set _fields
+        cls._fields = {}
+        for attr in dir(cls):
+            if attr.startswith('_'):
+                continue
+            if isinstance(getattr(cls, attr), fields.Field):
+                cls._fields[attr] = getattr(cls, attr)
 
-    def _getdefaults(self):
-        if self.__defaults:
-            return self.__defaults
-        res = {}
-        fields_names = self._columns.keys()
-        fields_names += self._inherit_fields.keys()
+        # Set _inherit_fields
+        cls._inherit_fields = {}
+        for model_name in cls._inherits:
+            Model = pool.get(model_name)
+            cls._inherit_fields.update(Model._inherit_fields)
+            for field_name in Model._fields.keys():
+                cls._inherit_fields[field_name] = (model_name,
+                    cls._inherits[model_name],
+                    Model._fields[field_name])
+            for field_name in Model._inherit_fields.keys():
+                cls._inherit_fields[field_name] = (model_name,
+                    cls._inherits[model_name],
+                    Model._inherit_fields[field_name][2])
+        # Update models that uses this one in _inherits
+        for _, Model in pool.iterobject():
+            if cls.__name__ in Model._inherits:
+                Model.__post_setup__()
+
+        # Set _defaults
+        cls._defaults = {}
+        fields_names = cls._fields.keys()
+        fields_names += cls._inherit_fields.keys()
         for field_name in fields_names:
-            default_method = getattr(self, 'default_%s' % field_name, False)
+            default_method = getattr(cls, 'default_%s' % field_name, False)
+            if not default_method and field_name in cls._inherit_fields:
+                icls = pool.get(cls._inherit_fields[field_name][0])
+                default_method = getattr(icls, 'default_%s' % field_name,
+                    False)
             if isinstance(default_method, collections.Callable):
-                res[field_name] = default_method
-        self.__defaults = res
-        return res
+                cls._defaults[field_name] = default_method
 
-    _defaults = property(fget=_getdefaults)
+        for k in cls._defaults:
+            assert (k in cls._fields) or (k in cls._inherit_fields), \
+                'Default function defined in %s but field %s does not exist!' \
+                % (cls.__name__, k,)
 
-    def __new__(cls):
-        Pool.register(cls, type='model')
-
-    def __init__(self):
-        super(Model, self).__init__()
-        self._rpc = {
-            'default_get': False,
-            'fields_get': False,
-            'on_change_with': False,
-        }
-        self._inherit_fields = []
-        self._error_messages = {}
-        # reinit the cache on _columns and _defaults
-        self.__columns = None
-        self.__defaults = None
-
-        if not self._description:
-            self._description = self._name
-
-        self._inherits_reload()
-        for k in self._defaults:
-            assert (k in self._columns) or (k in self._inherit_fields), \
-            'Default function defined in %s but field %s does not exist!' % \
-                (self._name, k,)
-        self._update_rpc()
-
-    def _update_rpc(self):
-        for field_name in self._columns.keys() + self._inherit_fields.keys():
-            if field_name in self._columns:
-                field = self._columns[field_name]
+        # Update __rpc__
+        for field_name in cls._fields.keys() + cls._inherit_fields.keys():
+            if field_name in cls._fields:
+                field = cls._fields[field_name]
             else:
-                field = self._inherit_fields[field_name][2]
+                field = cls._inherit_fields[field_name][2]
             if isinstance(field, (fields.Selection, fields.Reference)) \
                     and not isinstance(field.selection, (list, tuple)) \
-                    and field.selection not in self._rpc:
-                self._rpc[field.selection] = False
+                    and field.selection not in cls.__rpc__:
+                cls.__rpc__[field.selection] = RPC()
 
             for attribute in ('on_change', 'on_change_with', 'autocomplete'):
                 function_name = '%s_%s' % (attribute, field_name)
-                if (getattr(field, attribute, False)
-                         and isinstance(getattr(self, function_name, False),
-                             collections.Callable)):
-                    self._rpc.setdefault(function_name, False)
+                if getattr(field, attribute, False):
+                    cls.__rpc__.setdefault(function_name, RPC(instantiate=0))
 
-    def __getattr__(self, name):
-        pool = Pool()
-        # Search if a function exists in inherits parents
-        for model_name, field_name in self._inherits.iteritems():
-            model_obj = pool.get(model_name)
-            if (hasattr(model_obj, name)
-                    and isinstance(getattr(model_obj, name),
-                        collections.Callable)):
-                return getattr(model_obj, name)
-        raise AttributeError(name)
+        # Set name to fields
+        for name, field in cls._fields.iteritems():
+            if field.name is None:
+                field.name = name
+            else:
+                assert field.name == name, (
+                    'Duplicate fields on %s: %s, %s'
+                    % (cls, field.name, name))
 
-    def _inherits_reload(self):
-        """
-        Reconstruct _inherit_fields
-        """
-        res = {}
-        pool = Pool()
-        for model in self._inherits:
-            res.update(pool.get(model)._inherit_fields)
-            for field_name in pool.get(model)._columns.keys():
-                res[field_name] = (model, self._inherits[model],
-                        pool.get(model)._columns[field_name])
-            for field_name in pool.get(model)._inherit_fields.keys():
-                res[field_name] = (model, self._inherits[model],
-                        pool.get(model)._inherit_fields[field_name][2])
-        self._inherit_fields = res
-        self._reset_columns()
-        self._update_rpc()
-        # Update objects that uses this one to update their _inherits fields
-        for obj_name in pool.object_name_list():
-            obj = pool.get(obj_name)
-            if self._name in obj._inherits:
-                obj._inherits_reload()
-
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         """
         Add model in ir.model and ir.model.field.
-
-        :param module_name: the module name
         """
-        translation_obj = Pool().get('ir.translation')
+        pool = Pool()
+        Translation = pool.get('ir.translation')
+        Property = pool.get('ir.property')
 
         cursor = Transaction().cursor
         # Add model in ir_model
         cursor.execute("SELECT id FROM ir_model WHERE model = %s",
-                (self._name,))
+                (cls.__name__,))
         model_id = None
         if cursor.rowcount == -1 or cursor.rowcount is None:
             data = cursor.fetchone()
@@ -159,28 +157,31 @@
         elif cursor.rowcount != 0:
             model_id, = cursor.fetchone()
         if not model_id:
-            cursor.execute("INSERT INTO ir_model " \
-                    "(model, name, info, module) VALUES (%s, %s, %s, %s)",
-                    (self._name, self._description, self.__doc__,
-                        module_name))
+            cursor.execute("INSERT INTO ir_model "
+                "(model, name, info, module) VALUES (%s, %s, %s, %s)",
+                (cls.__name__, cls.__doc__.splitlines()[0], cls.__doc__,
+                    module_name))
+            Property._models_get_cache.clear()
             cursor.execute("SELECT id FROM ir_model WHERE model = %s",
-                    (self._name,))
+                    (cls.__name__,))
             (model_id,) = cursor.fetchone()
-        else:
-            cursor.execute('UPDATE ir_model ' \
-                    'SET name = %s, ' \
-                        'info = %s ' \
-                    'WHERE id = %s',
-                    (self._description, self.__doc__, model_id))
+        elif cls.__doc__:
+            cursor.execute('UPDATE ir_model '
+                'SET name = %s, '
+                    'info = %s '
+                'WHERE id = %s',
+                (cls.__doc__.splitlines()[0], cls.__doc__, model_id))
 
         # Update translation of model
-        for name, src in [(self._name + ',name', self._description)]:
-            cursor.execute('SELECT id FROM ir_translation '
-                'WHERE lang = %s '
-                    'AND type = %s '
-                    'AND name = %s '
-                    'AND (res_id IS NULL OR res_id = %s)',
-                ('en_US', 'model', name, 0))
+        if cls.__doc__:
+            name = cls.__name__ + ',name'
+            src = cls.__doc__.splitlines()[0]
+            cursor.execute('SELECT id FROM ir_translation ' \
+                    'WHERE lang = %s ' \
+                        'AND type = %s ' \
+                        'AND name = %s ' \
+                        'AND (res_id IS NULL OR res_id = %s)',
+                    ('en_US', 'model', name, 0))
             trans_id = None
             if cursor.rowcount == -1 or cursor.rowcount is None:
                 data = cursor.fetchone()
@@ -188,7 +189,7 @@
                     trans_id, = data
             elif cursor.rowcount != 0:
                 trans_id, = cursor.fetchone()
-            src_md5 = translation_obj.get_src_md5(src)
+            src_md5 = Translation.get_src_md5(src)
             if trans_id is None:
                 cursor.execute('INSERT INTO ir_translation '
                     '(name, lang, type, src, src_md5, value, module, fuzzy) '
@@ -209,21 +210,21 @@
                 'FROM ir_model_field AS f, ir_model AS m ' \
                 'WHERE f.model = m.id ' \
                     'AND m.model = %s ',
-                        (self._name,))
+                        (cls.__name__,))
         model_fields = {}
         for field in cursor.dictfetchall():
             model_fields[field['name']] = field
 
         # Prefetch field translations
-        if self._columns:
+        if cls._fields:
             cursor.execute('SELECT id, name, src, type FROM ir_translation ' \
                     'WHERE lang = %s ' \
                         'AND type IN (%s, %s, %s) ' \
                         'AND name IN ' \
-                            '(' + ','.join(('%s',) * len(self._columns)) + ')',
+                            '(' + ','.join(('%s',) * len(cls._fields)) + ')',
                             ('en_US', 'field', 'help', 'selection') + \
-                                    tuple([self._name + ',' + x \
-                                        for x in self._columns]))
+                                    tuple([cls.__name__ + ',' + x \
+                                        for x in cls._fields]))
         trans_fields = {}
         trans_help = {}
         trans_selection = {}
@@ -236,8 +237,8 @@
                 trans_selection.setdefault(trans['name'], {})
                 trans_selection[trans['name']][trans['src']] = trans
 
-        for field_name in self._columns:
-            field = self._columns[field_name]
+        for field_name in cls._fields:
+            field = cls._fields[field_name]
             relation = ''
             if hasattr(field, 'model_name'):
                 relation = field.model_name
@@ -262,8 +263,8 @@
                         'WHERE id = %s ',
                         (field.string, field._type, relation,
                             field.help, model_fields[field_name]['id']))
-            trans_name = self._name + ',' + field_name
-            string_md5 = translation_obj.get_src_md5(field.string)
+            trans_name = cls.__name__ + ',' + field_name
+            string_md5 = Translation.get_src_md5(field.string)
             if trans_name not in trans_fields:
                 cursor.execute('INSERT INTO ir_translation '
                     '(name, lang, type, src, src_md5, value, module, fuzzy) '
@@ -275,7 +276,7 @@
                     'SET src = %s, src_md5 = %s '
                     'WHERE id = %s ',
                     (field.string, string_md5, trans_fields[trans_name]['id']))
-            help_md5 = translation_obj.get_src_md5(field.help)
+            help_md5 = Translation.get_src_md5(field.help)
             if trans_name not in trans_help:
                 if field.help:
                     cursor.execute('INSERT INTO ir_translation '
@@ -297,7 +298,7 @@
                 for (_, val) in field.selection:
                     if trans_name not in trans_selection \
                             or val not in trans_selection[trans_name]:
-                        val_md5 = translation_obj.get_src_md5(val)
+                        val_md5 = Translation.get_src_md5(val)
                         cursor.execute('INSERT INTO ir_translation '
                             '(name, lang, type, src, src_md5, value, module, '
                                 'fuzzy) '
@@ -307,7 +308,7 @@
         # Clean ir_model_field from field that are no more existing.
         for field_name in model_fields:
             if model_fields[field_name]['module'] == module_name \
-                    and field_name not in self._columns:
+                    and field_name not in cls._fields:
                 #XXX This delete field even when it is defined later
                 # in the module
                 cursor.execute('DELETE FROM ir_model_field '\
@@ -319,145 +320,135 @@
                 'WHERE lang = %s ' \
                     'AND type = %s ' \
                     'AND name = %s',
-                ('en_US', 'error', self._name))
+                ('en_US', 'error', cls.__name__))
         trans_error = {}
         for trans in cursor.dictfetchall():
             trans_error[trans['src']] = trans
 
-        errors = self._get_error_messages()
+        errors = cls._get_error_messages()
         for error in set(errors):
             if error not in trans_error:
-                error_md5 = translation_obj.get_src_md5(error)
+                error_md5 = Translation.get_src_md5(error)
                 cursor.execute('INSERT INTO ir_translation '
                     '(name, lang, type, src, src_md5, value, module, fuzzy) '
                     'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
-                    (self._name, 'en_US', 'error', error, error_md5, '',
+                    (cls.__name__, 'en_US', 'error', error, error_md5, '',
                         module_name, False))
 
-    def _get_error_messages(self):
-        return self._error_messages.values()
+    @classmethod
+    def _get_error_messages(cls):
+        return cls._error_messages.values()
 
-    def default_get(self, fields_names, with_rec_name=True):
+    @classmethod
+    def default_get(cls, fields_names, with_rec_name=True):
         '''
         Return a dict with the default values for each field in fields_names.
-
-        :param fields_names: a list of fields names
-        :param with_rec_name: a boolean to add rec_name value
-        :return: a dictionary with field name as key
-            and default value as value
+        If with_rec_name is True, rec_name will be added.
         '''
         pool = Pool()
-        property_obj = pool.get('ir.property')
+        Property = pool.get('ir.property')
         value = {}
 
         # get the default values defined in the object
         for field_name in fields_names:
-            if field_name in self._defaults:
-                value[field_name] = self._defaults[field_name]()
-            if field_name in self._columns:
-                field = self._columns[field_name]
+            if field_name in cls._defaults:
+                value[field_name] = cls._defaults[field_name]()
+            if field_name in cls._fields:
+                field = cls._fields[field_name]
             else:
-                field = self._inherit_fields[field_name][2]
+                field = cls._inherit_fields[field_name][2]
             if (field._type == 'boolean'
                     and not field_name in value):
                 value[field_name] = False
             if isinstance(field, fields.Property):
-                value[field_name] = property_obj.get(field_name, self._name)
+                value[field_name] = Property.get(field_name, cls.__name__)
             if (with_rec_name
                     and field._type in ('many2one',)
                     and value.get(field_name)):
-                obj = pool.get(field.model_name)
-                if 'rec_name' in obj._columns:
-                    value[field_name + '.rec_name'] = obj.browse(
+                Target = pool.get(field.model_name)
+                if 'rec_name' in Target._fields:
+                    value[field_name + '.rec_name'] = Target(
                         value[field_name]).rec_name
 
-        value = self._default_on_change(value)
+        value = cls._default_on_change(value)
         if not with_rec_name:
             for field in value.keys():
                 if field.endswith('.rec_name'):
                     del value[field]
         return value
 
-    def _default_on_change(self, value):
+    @classmethod
+    def _default_on_change(cls, value):
         """
         Call on_change function for the default value
         and return new default value
-
-        :param value: a dictionnary with the default value
-        :return: a new dictionnary of default value
         """
         pool = Pool()
         res = value.copy()
         val = {}
-        for i in self._inherits.keys():
+        for i in cls._inherits.keys():
             val.update(pool.get(i)._default_on_change(value))
         for field in value.keys():
-            if field in self._columns:
-                if self._columns[field].on_change:
-                    args = {}
-                    for arg in self._columns[field].on_change:
-                        args[arg] = value.get(arg, None)
-                        if arg in self._columns \
-                                and self._columns[arg]._type == 'many2one':
-                            if isinstance(args[arg], (list, tuple)):
-                                args[arg] = args[arg][0]
-                    val.update(getattr(self, 'on_change_' + field)(args))
-                if self._columns[field]._type in ('one2many',):
-                    obj = pool.get(self._columns[field].model_name)
+            if field in cls._fields:
+                if cls._fields[field].on_change:
+                    inst = cls()
+                    for fname in cls._fields[field].on_change:
+                        setattr(inst, fname, value.get(fname))
+                    val.update(getattr(inst, 'on_change_' + field)())
+                if cls._fields[field]._type in ('one2many',):
+                    Target = pool.get(cls._fields[field].model_name)
                     for val2 in res[field]:
-                        val2.update(obj._default_on_change(val2))
+                        val2.update(Target._default_on_change(val2))
         res.update(val)
         return res
 
-    def fields_get(self, fields_names=None):
+    @classmethod
+    def fields_get(cls, fields_names=None):
         """
         Return the definition of each field on the model.
-
-        :param fields_names: a list of field names or None for all fields
-        :return: a dictionary with field name as key and definition as value
         """
         res = {}
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        field_access_obj = pool.get('ir.model.field.access')
+        Translation = pool.get('ir.translation')
+        FieldAccess = pool.get('ir.model.field.access')
 
-        for parent in self._inherits:
+        for parent in cls._inherits:
             res.update(pool.get(parent).fields_get(fields_names))
 
         #Add translation to cache
         language = Transaction().language
         trans_args = []
-        for field in (x for x in self._columns.keys()
+        for field in (x for x in cls._fields.keys()
                 if ((not fields_names) or x in fields_names)):
-            trans_args.append((self._name + ',' + field, 'field', language,
+            trans_args.append((cls.__name__ + ',' + field, 'field', language,
                 None))
-            trans_args.append((self._name + ',' + field, 'help', language,
+            trans_args.append((cls.__name__ + ',' + field, 'help', language,
                 None))
-            if hasattr(self._columns[field], 'selection'):
-                if isinstance(self._columns[field].selection, (tuple, list)) \
-                        and ((hasattr(self._columns[field],
+            if hasattr(cls._fields[field], 'selection'):
+                if isinstance(cls._fields[field].selection, (tuple, list)) \
+                        and ((hasattr(cls._fields[field],
                             'translate_selection') \
-                            and self._columns[field].translate_selection) \
-                            or not hasattr(self._columns[field],
+                            and cls._fields[field].translate_selection) \
+                            or not hasattr(cls._fields[field],
                                 'translate_selection')):
-                    sel = self._columns[field].selection
+                    sel = cls._fields[field].selection
                     for (key, val) in sel:
-                        trans_args.append((self._name + ',' + field,
+                        trans_args.append((cls.__name__ + ',' + field,
                             'selection', language, val))
-        translation_obj.get_sources(trans_args)
+        Translation.get_sources(trans_args)
 
         encoder = PYSONEncoder()
 
-        fwrite_accesses = field_access_obj.check(self._name, fields_names or
-                self._columns.keys(), 'write', access=True)
-        fcreate_accesses = field_access_obj.check(self._name, fields_names or
-            self._columns.keys(), 'create', access=True)
-        fdelete_accesses = field_access_obj.check(self._name, fields_names or
-            self._columns.keys(), 'delete', access=True)
-        for field in (x for x in self._columns.keys()
+        fwrite_accesses = FieldAccess.check(cls.__name__, fields_names or
+            cls._fields.keys(), 'write', access=True)
+        fcreate_accesses = FieldAccess.check(cls.__name__, fields_names or
+            cls._fields.keys(), 'create', access=True)
+        fdelete_accesses = FieldAccess.check(cls.__name__, fields_names or
+            cls._fields.keys(), 'delete', access=True)
+        for field in (x for x in cls._fields.keys()
                 if ((not fields_names) or x in fields_names)):
             res[field] = {
-                'type': self._columns[field]._type,
+                'type': cls._fields[field]._type,
                 'name': field,
                 }
             for arg in (
@@ -478,8 +469,8 @@
                     'loading',
                     'filename',
                     ):
-                if getattr(self._columns[field], arg, None) is not None:
-                    res[field][arg] = copy.copy(getattr(self._columns[field],
+                if getattr(cls._fields[field], arg, None) is not None:
+                    res[field][arg] = copy.copy(getattr(cls._fields[field],
                         arg))
             if not fwrite_accesses.get(field, True):
                 res[field]['readonly'] = True
@@ -487,78 +478,78 @@
                         'readonly' in res[field]['states']:
                     del res[field]['states']['readonly']
             for arg in ('digits', 'invisible'):
-                if hasattr(self._columns[field], arg) \
-                        and getattr(self._columns[field], arg):
-                    res[field][arg] = copy.copy(getattr(self._columns[field],
+                if hasattr(cls._fields[field], arg) \
+                        and getattr(cls._fields[field], arg):
+                    res[field][arg] = copy.copy(getattr(cls._fields[field],
                         arg))
-            if isinstance(self._columns[field],
+            if isinstance(cls._fields[field],
                     (fields.Function, fields.One2Many)) \
-                    and not self._columns[field].order_field:
+                    and not cls._fields[field].order_field:
                 res[field]['sortable'] = False
-            if ((isinstance(self._columns[field], fields.Function)
-                    and not self._columns[field].searcher)
-                    or self._columns[field]._type in ('binary', 'sha')):
+            if ((isinstance(cls._fields[field], fields.Function)
+                    and not cls._fields[field].searcher)
+                    or cls._fields[field]._type in ('binary', 'sha')):
                 res[field]['searchable'] = False
             else:
                 res[field]['searchable'] = True
 
             if Transaction().context.get('language'):
                 # translate the field label
-                res_trans = translation_obj.get_source(
-                        self._name + ',' + field, 'field',
-                        Transaction().context['language'])
+                res_trans = Translation.get_source(
+                    cls.__name__ + ',' + field, 'field',
+                    Transaction().context['language'])
                 if res_trans:
                     res[field]['string'] = res_trans
-                help_trans = translation_obj.get_source(
-                        self._name + ',' + field, 'help',
-                        Transaction().context['language'])
+                help_trans = Translation.get_source(
+                    cls.__name__ + ',' + field, 'help',
+                    Transaction().context['language'])
                 if help_trans:
                     res[field]['help'] = help_trans
 
-            if hasattr(self._columns[field], 'selection'):
-                if isinstance(self._columns[field].selection, (tuple, list)):
-                    sel = copy.copy(self._columns[field].selection)
+            if hasattr(cls._fields[field], 'selection'):
+                if isinstance(cls._fields[field].selection, (tuple, list)):
+                    sel = copy.copy(cls._fields[field].selection)
                     if Transaction().context.get('language') and \
-                            ((hasattr(self._columns[field],
+                            ((hasattr(cls._fields[field],
                                 'translate_selection') \
-                                and self._columns[field].translate_selection) \
-                                or not hasattr(self._columns[field],
+                                and cls._fields[field].translate_selection) \
+                                or not hasattr(cls._fields[field],
                                     'translate_selection')):
                         # translate each selection option
                         sel2 = []
                         for (key, val) in sel:
-                            val2 = translation_obj.get_source(
-                                    self._name + ',' + field, 'selection',
-                                    language, val)
+                            val2 = Translation.get_source(
+                                cls.__name__ + ',' + field, 'selection',
+                                language, val)
                             sel2.append((key, val2 or val))
                         sel = sel2
                     res[field]['selection'] = sel
                 else:
                     # call the 'dynamic selection' function
                     res[field]['selection'] = copy.copy(
-                            self._columns[field].selection)
+                            cls._fields[field].selection)
             if res[field]['type'] in (
                     'one2many',
                     'many2many',
                     'many2one',
                     'one2one',
                     ):
-                if hasattr(self._columns[field], 'model_name'):
-                    relation = copy.copy(self._columns[field].model_name)
+                if hasattr(cls._fields[field], 'model_name'):
+                    relation = copy.copy(cls._fields[field].model_name)
                 else:
                     relation = copy.copy(
-                        self._columns[field].get_target()._name)
+                        cls._fields[field].get_target().__name__)
                 res[field]['relation'] = relation
-                res[field]['domain'] = copy.copy(self._columns[field].domain)
-                res[field]['context'] = copy.copy(self._columns[field].context)
+                res[field]['domain'] = copy.copy(cls._fields[field].domain)
+                res[field]['context'] = copy.copy(cls._fields[field].context)
                 res[field]['create'] = fcreate_accesses.get(field, True)
                 res[field]['delete'] = fdelete_accesses.get(field, True)
             if res[field]['type'] == 'one2many' \
-                    and hasattr(self._columns[field], 'field'):
+                    and hasattr(cls._fields[field], 'field'):
                 res[field]['relation_field'] = copy.copy(
-                        self._columns[field].field)
+                        cls._fields[field].field)
             if res[field]['type'] in ('datetime', 'time'):
-                res[field]['format'] = copy.copy(self._columns[field].format)
+                res[field]['format'] = copy.copy(cls._fields[field].format)
 
             # convert attributes into pyson
             for attr in ('states', 'domain', 'context', 'digits', 'size',
@@ -573,9 +564,128 @@
                     del res[i]
         return res
 
-    def on_change_with(self, fieldnames, values):
+    def on_change_with(self, fieldnames):
         changes = {}
         for fieldname in fieldnames:
-            changes[fieldname] = getattr(self,
-                'on_change_with_%s' % fieldname)(values)
+            method_name = 'on_change_with_%s' % fieldname
+            changes[fieldname] = getattr(self, method_name)()
         return changes
+
+    def __init__(self, id=None, **kwargs):
+        super(Model, self).__init__()
+        if id is not None:
+            id = int(id)
+        self.__dict__['id'] = id
+        self._values = None
+        parent_values = {}
+        for name, value in kwargs.iteritems():
+            if not name.startswith('_parent_'):
+                setattr(self, name, value)
+            else:
+                parent_values[name] = value
+        for name, value in parent_values.iteritems():
+            parent_name, field = name.split('.', 1)
+            parent_name = parent_name[8:]  # Strip '_parent_'
+            parent = getattr(self, parent_name, None)
+            if parent is not None:
+                setattr(parent, field, value)
+            else:
+                setattr(self, parent_name, {field: value})
+
+    def __getattr__(self, name):
+        if name == 'id':
+            return self.__dict__['id']
+        if (name not in self._fields
+                and name not in self._inherit_fields):
+            # Search for method on inherits parents
+            pool = Pool()
+            for model_name, _ in self._inherits.iteritems():
+                iModel = pool.get(model_name)
+                if isinstance(getattr(iModel, name, None),
+                        collections.Callable):
+                    for cls in iModel.__mro__:
+                        if name in cls.__dict__:
+                            # Return a method bounded to current class
+                            return cls.__dict__[name].__get__(self,
+                                self.__class__)
+        elif self._values and name in self._values:
+            return self._values.get(name)
+        raise AttributeError("'%s' Model has no attribute '%s': %s"
+            % (self.__name__, name, self._values))
+
+    def __setattr__(self, name, value):
+        if name == 'id':
+            self.__dict__['id'] = value
+            return
+        if (name not in self._fields
+                and name in self._inherit_fields):
+            field = self._inherit_fields[name][2]
+            field.__set__(self, value)
+            return
+        super(Model, self).__setattr__(name, value)
+
+    def __getitem__(self, name):
+        warnings.warn('Use __getattr__ instead of __getitem__',
+            DeprecationWarning, stacklevel=2)
+        return getattr(self, name)
+
+    def __contains__(self, name):
+        return (name in self._fields
+            or name in self._inherit_fields)
+
+    def __int__(self):
+        return int(self.id)
+
+    def __str__(self):
+        return '%s,%s' % (self.__name__, self.id)
+
+    def __unicode__(self):
+        return u'%s,%s' % (self.__name__, self.id)
+
+    def __repr__(self):
+        if self.id < 0:
+            return "Pool().get('%s')(**%s)" % (self.__name__,
+                repr(self._default_values))
+        else:
+            return "Pool().get('%s')(%s)" % (self.__name__, self.id)
+
+    def __eq__(self, other):
+        if not isinstance(other, Model):
+            return NotImplemented
+        if self.id is None or other.id is None:
+            return False
+        return (self.__name__, self.id) == (other.__name__, other.id)
+
+    def __ne__(self, other):
+        if not isinstance(other, Model):
+            return NotImplemented
+        if self.id is None or other.id is None:
+            return True
+        return (self.__name__, self.id) != (other.__name__, other.id)
+
+    def __hash__(self):
+        return hash((self.__name__, self.id))
+
+    def __nonzero__(self):
+        return True
+
+    @property
+    def _default_values(self):
+        if self.id >= 0:
+            return self.id
+        values = {}
+        if self._values:
+            for fname, value in self._values.iteritems():
+                if fname in self._fields:
+                    field = self._fields[fname]
+                else:
+                    field = self._inherit_fields[fname][2]
+                if isinstance(field, fields.Reference):
+                    if value is not None:
+                        value = str(value)
+                elif isinstance(value, Model):
+                    value = value._default_values
+                elif isinstance(value, list):
+                    value = [r._default_values for r in value]
+                values[fname] = value
+        return values
diff -r fb51f5b4aa4c trytond/model/modelsingleton.py
--- a/trytond/model/modelsingleton.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/modelsingleton.py	Mon Sep 24 20:36:36 2012 +0200
@@ -8,98 +8,87 @@
     Define a singleton model in Tryton.
     """
 
-    def get_singleton_id(self):
+    @classmethod
+    def get_singleton(cls):
         '''
-        Return the id of the unique record if there is one.
+        Return the instance of the unique record if there is one.
         '''
-        singleton_ids = super(ModelSingleton, self).search([], limit=1)
-        if singleton_ids:
-            return singleton_ids[0]
-        return False
+        singletons = super(ModelSingleton, cls).search([], limit=1)
+        if singletons:
+            return singletons[0]
 
-    def create(self, values):
-        singleton_id = self.get_singleton_id()
-        if singleton_id:
-            self.write(singleton_id, values)
+    @classmethod
+    def create(cls, values):
+        singleton = cls.get_singleton()
+        if singleton:
+            cls.write([singleton], values)
         else:
-            singleton_id = super(ModelSingleton, self).create(values)
-        return singleton_id
+            singleton = super(ModelSingleton, cls).create(values)
+        return singleton
 
-    def read(self, ids, fields_names=None):
-        singleton_id = self.get_singleton_id()
-        if not singleton_id:
-            res = self.default_get(fields_names, with_rec_name=False)
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        singleton = cls.get_singleton()
+        if not singleton:
+            res = cls.default_get(fields_names, with_rec_name=False)
             if not fields_names:
-                fields_names = (set(self._columns.keys()
-                    + self._inherit_fields.keys()))
+                fields_names = (set(cls._fields.keys()
+                    + cls._inherit_fields.keys()))
             for field_name in fields_names:
                 if field_name not in res:
                     res[field_name] = False
-            if not isinstance(ids, (int, long)):
-                res['id'] = ids[0]
-                res = [res]
-            else:
-                res['id'] = ids
-            return res
-        if isinstance(ids, (int, long)):
-            ids2 = singleton_id
-        else:
-            ids2 = [singleton_id]
-        res = super(ModelSingleton, self).read(ids2, fields_names=fields_names)
-        if isinstance(ids, (int, long)):
-            res['id'] = ids
-        else:
-            res[0]['id'] = ids[0]
+            res['id'] = ids[0]
+            return [res]
+        res = super(ModelSingleton, cls).read([singleton.id],
+            fields_names=fields_names)
+        res[0]['id'] = ids[0]
         return res
 
-    def write(self, ids, values):
-        singleton_id = self.get_singleton_id()
-        if not singleton_id:
-            return self.create(values)
-        if isinstance(ids, (int, long)):
-            ids = singleton_id
-        else:
-            ids = [singleton_id]
-        return super(ModelSingleton, self).write(ids, values)
+    @classmethod
+    def write(cls, records, values):
+        singleton = cls.get_singleton()
+        if not singleton:
+            return cls.create(values)
+        return super(ModelSingleton, cls).write([singleton], values)
 
-    def delete(self, ids):
-        singleton_id = self.get_singleton_id()
-        if not singleton_id:
-            return True
-        if isinstance(ids, (int, long)):
-            ids = singleton_id
-        else:
-            ids = [singleton_id]
-        return super(ModelSingleton, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        singleton = cls.get_singleton()
+        if not singleton:
+            return
+        return super(ModelSingleton, cls).delete([singleton])
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, records, default=None):
         if default:
-            self.write(ids, default)
-        return ids
+            cls.write(records, default)
+        return records
 
-    def search(self, domain, offset=0, limit=None, order=None, count=False):
-        res = super(ModelSingleton, self).search(domain, offset=offset,
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False):
+        res = super(ModelSingleton, cls).search(domain, offset=offset,
                 limit=limit, order=order, count=count)
         if not res:
             if count:
                 return 1
-            return [1]
+            return [cls(1)]
         return res
 
-    def default_get(self, fields_names, with_rec_name=True):
+    @classmethod
+    def default_get(cls, fields_names, with_rec_name=True):
         if '_timestamp' in fields_names:
             fields_names = list(fields_names)
             fields_names.remove('_timestamp')
-        res = super(ModelSingleton, self).default_get(fields_names,
+        default = super(ModelSingleton, cls).default_get(fields_names,
                 with_rec_name=with_rec_name)
-        singleton_id = self.get_singleton_id()
-        if singleton_id:
+        singleton = cls.get_singleton()
+        if singleton:
             if with_rec_name:
                 fields_names = fields_names[:]
                 for field in fields_names[:]:
-                    if self._columns[field]._type in ('many2one',):
+                    if cls._fields[field]._type in ('many2one',):
                         fields_names.append(field + '.rec_name')
-            res = self.read(singleton_id, fields_names=fields_names)
-            for field in (x for x in res.keys() if x not in fields_names):
-                del res[field]
-        return res
+            default, = cls.read([singleton.id], fields_names=fields_names)
+            for field in (x for x in default.keys() if x not in fields_names):
+                del default[field]
+        return default
diff -r fb51f5b4aa4c trytond/model/modelsql.py
--- a/trytond/model/modelsql.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/modelsql.py	Mon Sep 24 20:36:36 2012 +0200
@@ -30,68 +30,70 @@
     _order_name = None  # Use to force order field when sorting on Many2One
     _history = False
 
-    def __init__(self):
-        super(ModelSQL, self).__init__()
-        self._sql_constraints = []
-        self._order = [('id', 'ASC')]
-        self._sql_error_messages = {}
+    @classmethod
+    def __setup__(cls):
+        super(ModelSQL, cls).__setup__()
+        cls._sql_constraints = []
+        cls._order = [('id', 'ASC')]
+        cls._sql_error_messages = {}
 
-        if not self._table:
-            self._table = self._name.replace('.', '_')
+        if not cls._table:
+            cls._table = cls.__name__.replace('.', '_')
 
-        assert self._table[-9:] != '__history', \
-                'Model _table %s cannot end with "__history"' % self._table
+        assert cls._table[-9:] != '__history', \
+                'Model _table %s cannot end with "__history"' % cls._table
 
-    def init(self, module_name):
-        super(ModelSQL, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ModelSQL, cls).__register__(module_name)
 
-        if self.table_query():
+        if cls.table_query():
             return
 
         pool = Pool()
 
         # create/update table in the database
-        table = TableHandler(Transaction().cursor, self, module_name)
-        if self._history:
-            history_table = TableHandler(Transaction().cursor, self,
+        table = TableHandler(Transaction().cursor, cls, module_name)
+        if cls._history:
+            history_table = TableHandler(Transaction().cursor, cls,
                     module_name, history=True)
         timestamp_field = FIELDS['timestamp']
         integer_field = FIELDS['integer']
         logs = (
             ('create_date', timestamp_field.sql_type(None),
                 timestamp_field.sql_format, lambda *a: datetime.datetime.now(),
-                self.create_date.string),
+                cls.create_date.string),
             ('write_date', timestamp_field.sql_type(None),
-                timestamp_field.sql_format, None, self.write_date.string),
+                timestamp_field.sql_format, None, cls.write_date.string),
             ('create_uid', (integer_field.sql_type(None)[0],
              'INTEGER REFERENCES res_user ON DELETE SET NULL',),
-             integer_field.sql_format, lambda *a: 0, self.create_uid.string),
+             integer_field.sql_format, lambda *a: 0, cls.create_uid.string),
             ('write_uid', (integer_field.sql_type(None)[0],
              'INTEGER REFERENCES res_user ON DELETE SET NULL'),
-             integer_field.sql_format, None, self.write_uid.string),
+             integer_field.sql_format, None, cls.write_uid.string),
             )
         for log in logs:
             table.add_raw_column(log[0], log[1], log[2],
                     default_fun=log[3], migrate=False, string=log[4])
-        if self._history:
+        if cls._history:
             history_logs = (
                     ('create_date', timestamp_field.sql_type(None),
-                        timestamp_field.sql_format, self.create_date.string),
+                        timestamp_field.sql_format, cls.create_date.string),
                     ('write_date', timestamp_field.sql_type(None),
-                        timestamp_field.sql_format, self.write_date.string),
+                        timestamp_field.sql_format, cls.write_date.string),
                     ('create_uid', (integer_field.sql_type(None)[0],
                      'INTEGER REFERENCES res_user ON DELETE SET NULL',),
-                     integer_field.sql_format, self.create_uid.string),
+                     integer_field.sql_format, cls.create_uid.string),
                     ('write_uid', (integer_field.sql_type(None)[0],
                      'INTEGER REFERENCES res_user ON DELETE SET NULL'),
-                     integer_field.sql_format, self.write_uid.string),
+                     integer_field.sql_format, cls.write_uid.string),
                     )
             for log in history_logs:
                 history_table.add_raw_column(log[0], log[1], log[2],
                         migrate=False, string=log[3])
             history_table.index_action('id', action='add')
 
-        for field_name, field in self._columns.iteritems():
+        for field_name, field in cls._fields.iteritems():
             default_fun = None
             if field_name in (
                     'id',
@@ -104,8 +106,8 @@
                 continue
 
             if not hasattr(field, 'set'):
-                if field_name in self._defaults:
-                    default_fun = self._defaults[field_name]
+                if field_name in cls._defaults:
+                    default_fun = cls._defaults[field_name]
 
                     def unpack_wrapper(fun):
                         def unpack_result(*a):
@@ -115,7 +117,7 @@
                                 result = fun(*a)
                             except Exception:
                                 return None
-                            clean_results = self._clean_defaults(
+                            clean_results = cls._clean_defaults(
                                 {field_name: result})
                             return clean_results[field_name]
                         return unpack_result
@@ -129,7 +131,7 @@
                         FIELDS[field._type].sql_type(field),
                         FIELDS[field._type].sql_format, default_fun,
                         field_size, string=field.string)
-                if self._history:
+                if cls._history:
                     history_table.add_raw_column(field_name,
                             FIELDS[field._type].sql_type(field), None,
                             string=field.string)
@@ -159,69 +161,71 @@
                 fields.Many2Many)):
                 raise Exception('Unknow field type !')
 
-        for field_name, field in self._columns.iteritems():
+        for field_name, field in cls._fields.iteritems():
             if isinstance(field, fields.Many2One) \
-                    and field.model_name == self._name \
+                    and field.model_name == cls.__name__ \
                     and field.left and field.right:
                 with Transaction().set_user(0):
-                    self._rebuild_tree(field_name, False, 0)
+                    cls._rebuild_tree(field_name, False, 0)
 
-        for ident, constraint, _ in self._sql_constraints:
+        for ident, constraint, _ in cls._sql_constraints:
             table.add_constraint(ident, constraint)
 
-        if self._history:
-            self._update_history_table()
+        if cls._history:
+            cls._update_history_table()
             cursor = Transaction().cursor
-            cursor.execute('SELECT id FROM "' + self._table + '"')
+            cursor.execute('SELECT id FROM "' + cls._table + '"')
             if cursor.fetchone():
-                cursor.execute('SELECT id FROM "' + self._table + '__history"')
+                cursor.execute('SELECT id FROM "' + cls._table + '__history"')
                 if not cursor.fetchone():
-                    columns = ['"' + str(x) + '"' for x in self._columns
-                            if not hasattr(self._columns[x], 'set')]
+                    columns = ['"' + str(x) + '"' for x in cls._fields
+                            if not hasattr(cls._fields[x], 'set')]
                     cursor.execute('INSERT ' \
-                            'INTO "' + self._table + '__history" '\
+                            'INTO "' + cls._table + '__history" '\
                             '(' + ','.join(columns) + ') ' \
                             'SELECT ' + ','.join(columns) + \
-                            ' FROM "' + self._table + '"')
-                    cursor.execute('UPDATE "' + self._table + '__history" ' \
+                            ' FROM "' + cls._table + '"')
+                    cursor.execute('UPDATE "' + cls._table + '__history" ' \
                             'SET write_date = NULL')
 
-    def _update_history_table(self):
-        if self._history:
-            table = TableHandler(Transaction().cursor, self)
-            history_table = TableHandler(Transaction().cursor, self,
+    @classmethod
+    def _update_history_table(cls):
+        if cls._history:
+            table = TableHandler(Transaction().cursor, cls)
+            history_table = TableHandler(Transaction().cursor, cls,
                     history=True)
             for column_name in table._columns:
                 string = ''
-                if column_name in self._columns:
-                    string = self._columns[column_name].string
+                if column_name in cls._fields:
+                    string = cls._fields[column_name].string
                 history_table.add_raw_column(column_name,
-                        (table._columns[column_name]['typname'],
-                            table._columns[column_name]['typname']),
-                            None, string=string)
+                    (table._columns[column_name]['typname'],
+                        table._columns[column_name]['typname']),
+                    None, string=string)
 
-    def _get_error_messages(self):
-        res = super(ModelSQL, self)._get_error_messages()
-        res += self._sql_error_messages.values()
-        for _, _, error in self._sql_constraints:
+    @classmethod
+    def _get_error_messages(cls):
+        res = super(ModelSQL, cls)._get_error_messages()
+        res += cls._sql_error_messages.values()
+        for _, _, error in cls._sql_constraints:
             res.append(error)
         return res
 
-    def table_query(self):
+    @staticmethod
+    def table_query():
         '''
         Return None if the model is a real table in the database
         or return a tuple with the SQL query and the arguments.
-
-        :return: None or a tuple with a SQL query and arguments
         '''
         return None
 
-    def create(self, values):
-        super(ModelSQL, self).create(values)
+    @classmethod
+    def create(cls, values):
+        super(ModelSQL, cls).create(values)
         cursor = Transaction().cursor
         pool = Pool()
 
-        if self.table_query():
+        if cls.table_query():
             return False
 
         values = values.copy()
@@ -235,22 +239,22 @@
         # Get default values
         default = []
         avoid_table = []
-        for (i, j) in self._inherits.items():
+        for (i, j) in cls._inherits.items():
             if j in values:
                 avoid_table.append(i)
-        for i in self._columns.keys():
+        for i in cls._fields.keys():
             if not i in values \
                     and i not in ('create_uid', 'create_date',
                             'write_uid', 'write_date'):
                 default.append(i)
-        for i in self._inherit_fields.keys():
+        for i in cls._inherit_fields.keys():
             if ((not i in values)
-                    and (not self._inherit_fields[i][0] in avoid_table)
-                    and i in self._defaults):
+                    and (not cls._inherit_fields[i][0] in avoid_table)
+                    and i in cls._defaults):
                 default.append(i)
 
         if len(default):
-            defaults = self.default_get(default, with_rec_name=False)
+            defaults = cls.default_get(default, with_rec_name=False)
             for field in defaults.keys():
                 if '.' in field:
                     del defaults[field]
@@ -259,86 +263,78 @@
                     del defaults[field]
                 if field in values:
                     del defaults[field]
-            values.update(self._clean_defaults(defaults))
+            values.update(cls._clean_defaults(defaults))
 
         (upd0, upd1, upd2) = ('', '', [])
         upd_todo = []
 
         # Create inherits
         tocreate = {}
-        for i in self._inherits:
-            if self._inherits[i] not in values:
+        for i in cls._inherits:
+            if cls._inherits[i] not in values:
                 tocreate[i] = {}
 
         for i in values.keys():
-            if i in self._inherit_fields:
-                (inherits, _, _) = self._inherit_fields[i]
-                if i in self._columns:
+            if i in cls._inherit_fields:
+                (inherits, _, _) = cls._inherit_fields[i]
+                if i in cls._fields:
                     continue
                 if inherits in tocreate:
                     tocreate[inherits][i] = values[i]
                 del values[i]
 
         for inherits in tocreate:
-            inherits_obj = pool.get(inherits)
+            Inherits = pool.get(inherits)
             with Transaction().set_user(0, set_context=True):
-                inherits_id = inherits_obj.create(tocreate[inherits])
-            values[self._inherits[inherits]] = inherits_id
+                inherits_record = Inherits.create(tocreate[inherits])
+            values[cls._inherits[inherits]] = inherits_record.id
 
         # Insert record
-        for field in values:
-            column = self._columns[field]
-            if not hasattr(self._columns[field], 'set'):
-                upd0 = upd0 + ',"' + field + '"'
+        for fname, value in values.iteritems():
+            field = cls._fields[fname]
+            if not hasattr(field, 'set'):
+                upd0 = upd0 + ',"' + fname + '"'
                 upd1 = upd1 + ', %s'
-                upd2.append(FIELDS[self._columns[field]._type].sql_format(
-                    values[field]))
+                upd2.append(FIELDS[field._type].sql_format(value))
             else:
-                upd_todo.append(field)
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'selection') \
-                    and self._columns[field].selection \
-                    and values[field]:
-                if self._columns[field]._type == 'reference':
-                    if isinstance(values[field], basestring):
-                        val = values[field].split(',')[0]
+                upd_todo.append(fname)
+            if (hasattr(field, 'selection')
+                    and field.selection
+                    and value):
+                if field._type == 'reference':
+                    if isinstance(value, basestring):
+                        value = value.split(',')[0]
                     else:
-                        val, _ = values[field]
+                        value, _ = value
+                if isinstance(field.selection, (tuple, list)):
+                    test = dict(field.selection)
                 else:
-                    val = values[field]
-                if isinstance(self._columns[field].selection, (tuple, list)):
-                    if val not in dict(self._columns[field].selection):
-                        raise Exception('ValidateError',
-                        'The value "%s" for the field "%s" ' \
-                                'is not in the selection' % \
-                                (val, field))
-                else:
-                    if val not in dict(getattr(self,
-                        self._columns[field].selection)()):
-                        raise Exception('ValidateError',
-                        'The value "%s" for the field "%s" ' \
-                                'is not in the selection' % \
-                                (val, field))
+                    test = dict(getattr(cls, field.selection)())
+                if value not in test:
+                    raise Exception('ValidateError',
+                        'The value "%s" for the field "%s" '
+                        'is not in the selection'
+                        % (value, fname))
         upd0 += ', create_uid, create_date'
         upd1 += ', %s, %s'
         upd2.append(Transaction().user)
         upd2.append(datetime.datetime.now())
         try:
             if cursor.has_returning():
-                cursor.execute('INSERT INTO "' + self._table + '" '
+                cursor.execute('INSERT INTO "' + cls._table + '" '
                     '(' + upd0[1:] + ') '
                     'VALUES (' + upd1[1:] + ') RETURNING id',
                     tuple(upd2))
                 id_new, = cursor.fetchone()
             else:
-                id_new = cursor.nextid(self._table)
+                id_new = cursor.nextid(cls._table)
                 if id_new:
-                    cursor.execute('INSERT INTO "' + self._table + '" '
+                    cursor.execute('INSERT INTO "' + cls._table + '" '
                         '(id' + upd0 + ') '
                         'VALUES (' + str(id_new) + upd1 + ')',
                         tuple(upd2))
                 else:
-                    cursor.execute('INSERT INTO "' + self._table + '" '
+                    cursor.execute('INSERT INTO "' + cls._table + '" '
                         '(' + upd0[1:] + ') '
                         'VALUES (' + upd1[1:] + ')',
                         tuple(upd2))
@@ -346,101 +342,99 @@
         except DatabaseIntegrityError, exception:
             with contextlib.nested(Transaction().new_cursor(),
                     Transaction().set_user(0)):
-                for field_name in self._columns:
-                    field = self._columns[field_name]
+                for field_name in cls._fields:
+                    field = cls._fields[field_name]
                     # Check required fields
                     if field.required and \
                             not hasattr(field, 'set') and \
                             field_name not in ('create_uid', 'create_date'):
                         if not values.get(field_name):
-                            self.raise_user_error('required_field',
-                                    error_args=self._get_error_args(
+                            cls.raise_user_error('required_field',
+                                    error_args=cls._get_error_args(
                                         field_name))
                     if isinstance(field, fields.Many2One) \
                             and values.get(field_name):
-                        model_obj = pool.get(field.model_name)
+                        Model = pool.get(field.model_name)
                         create_records = Transaction().create_records.get(
                                 field.model_name, set())
                         delete_records = Transaction().delete_records.get(
                                 field.model_name, set())
-                        if not ((model_obj.search([
-                            ('id', '=', values[field_name]),
-                            ], order=[])
-                            or values[field_name] in create_records)
-                            and values[field_name] not in delete_records):
-                            self.raise_user_error('foreign_model_missing',
-                                    error_args=self._get_error_args(
+                        if not ((Model.search([
+                                            ('id', '=', values[field_name]),
+                                            ], order=[])
+                                    or values[field_name] in create_records)
+                                and values[field_name] not in delete_records):
+                            cls.raise_user_error('foreign_model_missing',
+                                    error_args=cls._get_error_args(
                                         field_name))
-                for name, _, error in self._sql_constraints:
+                for name, _, error in cls._sql_constraints:
                     if name in exception[0]:
-                        self.raise_user_error(error)
-                for name, error in self._sql_error_messages:
+                        cls.raise_user_error(error)
+                for name, error in cls._sql_error_messages:
                     if name in exception[0]:
-                        self.raise_user_error(error)
+                        cls.raise_user_error(error)
             raise
 
-        domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
+        domain1, domain2 = pool.get('ir.rule').domain_get(cls.__name__,
                 mode='create')
         if domain1:
-            cursor.execute('SELECT id FROM "' + self._table + '" ' \
+            cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                     'WHERE id = %s AND (' + domain1 + ')',
                     [id_new] + domain2)
             if not cursor.fetchone():
-                self.raise_user_error('access_error', self._description)
+                cls.raise_user_error('access_error', cls.__name__)
 
-        Transaction().create_records.setdefault(self._name, set()).add(id_new)
+        Transaction().create_records.setdefault(
+            cls.__name__, set()).add(id_new)
 
         for field in values:
-            if getattr(self._columns[field], 'translate', False):
+            if getattr(cls._fields[field], 'translate', False):
                 pool.get('ir.translation').set_ids(
-                        self._name + ',' + field, 'model',
+                        cls.__name__ + ',' + field, 'model',
                         Transaction().language, [id_new], values[field])
 
         for field in upd_todo:
-            self._columns[field].set([id_new], self, field, values[field])
+            cls._fields[field].set([id_new], cls, field, values[field])
 
-        if self._history:
-            cursor.execute('INSERT INTO "' + self._table + '__history" '
+        if cls._history:
+            cursor.execute('INSERT INTO "' + cls._table + '__history" '
                 '(id' + upd0 + ') '
                 'SELECT id' + upd0 + ' '
-                'FROM "' + self._table + '" '
+                'FROM "' + cls._table + '" '
                 'WHERE id = %s', (id_new,))
 
-        self._validate([id_new])
+        record = cls(id_new)
+        cls._validate([record])
 
         # Check for Modified Preorder Tree Traversal
-        for k in self._columns:
-            field = self._columns[k]
+        for k in cls._fields:
+            field = cls._fields[k]
             if isinstance(field, fields.Many2One) \
-                    and field.model_name == self._name \
+                    and field.model_name == cls.__name__ \
                     and field.left and field.right:
-                self._update_tree(id_new, k, field.left, field.right)
+                cls._update_tree(id_new, k, field.left, field.right)
 
-        self.trigger_create(id_new)
+        cls.trigger_create(record)
 
-        return id_new
+        return record
 
-    def read(self, ids, fields_names=None):
+    @classmethod
+    def read(cls, ids, fields_names=None):
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
-        translation_obj = pool.get('ir.translation')
-        super(ModelSQL, self).read(ids, fields_names=fields_names)
+        Rule = pool.get('ir.rule')
+        Translation = pool.get('ir.translation')
+        super(ModelSQL, cls).read(ids, fields_names=fields_names)
         cursor = Transaction().cursor
 
         if not fields_names:
-            fields_names = list(set(self._columns.keys() \
-                    + self._inherit_fields.keys()))
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+            fields_names = list(set(cls._fields.keys() \
+                    + cls._inherit_fields.keys()))
 
         if not ids:
             return []
 
         # construct a clause for the rules :
-        domain1, domain2 = rule_obj.domain_get(self._name, mode='read')
+        domain1, domain2 = Rule.domain_get(cls.__name__, mode='read')
 
         fields_related = {}
         datetime_fields = []
@@ -451,27 +445,27 @@
                 field, field_related = field_name.split('.', 1)
                 fields_related.setdefault(field, [])
                 fields_related[field].append(field_related)
-            elif field_name in self._columns:
-                field = self._columns[field_name]
+            elif field_name in cls._fields:
+                field = cls._fields[field_name]
             else:
-                field = self._inherit_fields[field_name][2]
+                field = cls._inherit_fields[field_name][2]
             if hasattr(field, 'datetime_field') and field.datetime_field:
                 datetime_fields.append(field.datetime_field)
 
         # all inherited fields + all non inherited fields
         fields_pre = [x for x in \
                 fields_names + fields_related.keys() + datetime_fields \
-                if (x in self._columns and \
-                not hasattr(self._columns[x], 'set')) or \
+                if (x in cls._fields and \
+                not hasattr(cls._fields[x], 'set')) or \
                 (x == '_timestamp')] + \
-                self._inherits.values()
+                cls._inherits.values()
 
         res = []
         table_query = ''
         table_args = []
 
-        if self.table_query():
-            table_query, table_args = self.table_query()
+        if cls.table_query():
+            table_query, table_args = cls.table_query()
             table_query = '(' + table_query + ') AS '
 
         in_max = cursor.IN_MAX
@@ -479,33 +473,33 @@
         history_clause = ''
         history_limit = ''
         history_args = []
-        if (self._history
+        if (cls._history
                 and Transaction().context.get('_datetime')
                 and not table_query):
             in_max = 1
-            table_query = '"' + self._table + '__history" AS '
+            table_query = '"' + cls._table + '__history" AS '
             history_clause = ' AND (COALESCE(write_date, create_date) <= %s)'
             history_order = ' ORDER BY COALESCE(write_date, create_date) DESC'
             history_limit = cursor.limit_clause('', 1)
             history_args = [Transaction().context['_datetime']]
         if len(fields_pre):
-            fields_pre2 = ['"%s"."%s" AS "%s"' % (self._table, x, x)
+            fields_pre2 = ['"%s"."%s" AS "%s"' % (cls._table, x, x)
                     for x in fields_pre if x != '_timestamp']
             if '_timestamp' in fields_pre:
-                if not self.table_query():
+                if not cls.table_query():
                     fields_pre2 += ['CAST(EXTRACT(EPOCH FROM '
                             '(COALESCE("%s".write_date, "%s".create_date))) '
                             'AS VARCHAR) AS _timestamp' %
-                            (self._table, self._table)]
+                            (cls._table, cls._table)]
             if 'id' not in fields_pre:
-                fields_pre2 += ['"%s".id AS id' % self._table]
+                fields_pre2 += ['"%s".id AS id' % cls._table]
 
             for i in range(0, len(ids), in_max):
                 sub_ids = ids[i:i + in_max]
                 red_sql, red_ids = reduce_ids('id', sub_ids)
                 if domain1:
                     cursor.execute('SELECT ' + ','.join(fields_pre2)
-                        + ' FROM ' + table_query + '\"' + self._table + '\" '
+                        + ' FROM ' + table_query + '\"' + cls._table + '\" '
                         'WHERE ' + red_sql + history_clause +
                             ' AND (' + domain1 + ') '
                         + history_order
@@ -513,7 +507,7 @@
                         table_args + red_ids + history_args + domain2)
                 else:
                     cursor.execute('SELECT ' + ','.join(fields_pre2)
-                        + ' FROM ' + table_query + '\"' + self._table + '\" '
+                        + ' FROM ' + table_query + '\"' + cls._table + '\" '
                         'WHERE ' + red_sql + history_clause
                         + history_order
                         + history_limit,
@@ -522,7 +516,7 @@
                 if not len(dictfetchall) == len({}.fromkeys(sub_ids)):
                     if domain1:
                         cursor.execute('SELECT id '
-                            'FROM ' + table_query + '\"' + self._table + '\" '
+                            'FROM ' + table_query + '\"' + cls._table + '\" '
                             'WHERE ' + red_sql + history_clause
                             + history_order
                             + history_limit,
@@ -531,9 +525,8 @@
                         if rowcount == -1 or rowcount is None:
                             rowcount = len(cursor.fetchall())
                         if rowcount == len({}.fromkeys(sub_ids)):
-                            self.raise_user_error('access_error',
-                                    self._description)
-                    self.raise_user_error('read_error', self._description)
+                            cls.raise_user_error('access_error', cls.__name__)
+                    cls.raise_user_error('read_error', cls.__name__)
                 res.extend(dictfetchall)
         else:
             res = [{'id': x} for x in ids]
@@ -541,19 +534,19 @@
         for field in fields_pre:
             if field == '_timestamp':
                 continue
-            if getattr(self._columns[field], 'translate', False):
+            if getattr(cls._fields[field], 'translate', False):
                 ids = [x['id'] for x in res]
-                res_trans = translation_obj.get_ids(self._name + ',' + field,
+                res_trans = Translation.get_ids(cls.__name__ + ',' + field,
                     'model', Transaction().language, ids)
                 for i in res:
                     i[field] = res_trans.get(i['id'], False) or i[field]
 
-        for table in self._inherits:
-            field = self._inherits[table]
+        for table in cls._inherits:
+            field = cls._inherits[table]
             inherits_fields = list(
-                    set(self._inherit_fields.keys()).intersection(
+                    set(cls._inherit_fields.keys()).intersection(
                     set(fields_names + fields_related.keys() + datetime_fields)
-                    ).difference(set(self._columns.keys())))
+                    ).difference(set(cls._fields.keys())))
             if not inherits_fields:
                 for record in res:
                     if field not in fields_names + fields_related.keys() + \
@@ -585,27 +578,27 @@
         # all non inherited fields for which there is a get attribute
         fields_post = [x for x in \
                 fields_names + fields_related.keys() + datetime_fields \
-                if x in self._columns \
-                and hasattr(self._columns[x], 'get')]
+                if x in cls._fields \
+                and hasattr(cls._fields[x], 'get')]
         func_fields = {}
         for field in fields_post:
-            if isinstance(self._columns[field], fields.Function):
-                key = (self._columns[field].getter,
-                        getattr(self._columns[field], 'datetime_field', None))
+            if isinstance(cls._fields[field], fields.Function):
+                key = (cls._fields[field].getter,
+                        getattr(cls._fields[field], 'datetime_field', None))
                 func_fields.setdefault(key, [])
                 func_fields[key].append(field)
                 continue
-            if hasattr(self._columns[field], 'datetime_field') \
-                    and self._columns[field].datetime_field:
+            if hasattr(cls._fields[field], 'datetime_field') \
+                    and cls._fields[field].datetime_field:
                 for record in res:
                     with Transaction().set_context(_datetime=record[
-                                self._columns[field].datetime_field]):
-                        res2 = self._columns[field].get([record['id']], self,
+                                cls._fields[field].datetime_field]):
+                        res2 = cls._fields[field].get([record['id']], cls,
                             field, values=[record])
                     record[field] = res2[record['id']]
                 continue
             # get the value of that field for all records/ids
-            res2 = self._columns[field].get(ids, self, field, values=res)
+            res2 = cls._fields[field].get(ids, cls, field, values=res)
             for record in res:
                 record[field] = res2[record['id']]
         for key in func_fields:
@@ -616,12 +609,12 @@
                 for record in res:
                     with Transaction().set_context(
                             _datetime=record[datetime_field]):
-                        res2 = self._columns[field].get([record['id']], self,
+                        res2 = cls._fields[field].get([record['id']], cls,
                                 field_list, values=[record])
                     for field in res2:
                         record[field] = res2[field][record['id']]
                 continue
-            res2 = self._columns[field].get(ids, self, field_list, values=res)
+            res2 = cls._fields[field].get(ids, cls, field_list, values=res)
             for field in res2:
                 for record in res:
                     record[field] = res2[field][record['id']]
@@ -631,24 +624,24 @@
         for field in fields_related.keys() + datetime_fields:
             if field not in fields_names:
                 to_del.add(field)
-            if field not in self._columns:
+            if field not in cls._fields:
                 continue
             if field not in fields_related.keys():
                 continue
             fields_related2values.setdefault(field, {})
-            if self._columns[field]._type in ('many2one', 'one2one'):
-                if hasattr(self._columns[field], 'model_name'):
-                    obj = pool.get(self._columns[field].model_name)
+            if cls._fields[field]._type in ('many2one', 'one2one'):
+                if hasattr(cls._fields[field], 'model_name'):
+                    obj = pool.get(cls._fields[field].model_name)
                 else:
-                    obj = self._columns[field].get_target()
-                if hasattr(self._columns[field], 'datetime_field') \
-                        and self._columns[field].datetime_field:
+                    obj = cls._fields[field].get_target()
+                if hasattr(cls._fields[field], 'datetime_field') \
+                        and cls._fields[field].datetime_field:
                     for record in res:
                         if not record[field]:
                             continue
                         with Transaction().set_context(_datetime=record[
-                                    self._columns[field].datetime_field]):
-                            record2 = obj.read(record[field],
+                                    cls._fields[field].datetime_field]):
+                            record2, = obj.read([record[field]],
                                     fields_related[field])
                         record_id = record2['id']
                         del record2['id']
@@ -664,7 +657,7 @@
                         for record2 in res:
                             fields_related2values[
                                 field][record_id][record2['id']] = record
-            elif self._columns[field]._type == 'reference':
+            elif cls._fields[field]._type == 'reference':
                 for record in res:
                     if not record[field]:
                         continue
@@ -675,17 +668,17 @@
                     if record_id < 0:
                         continue
                     obj = pool.get(model_name)
-                    record2 = obj.read(record_id, fields_related[field])
+                    record2, = obj.read([record_id], fields_related[field])
                     del record2['id']
                     fields_related2values[field][record[field]] = record2
 
         if to_del or fields_related.keys() or datetime_fields:
             for record in res:
                 for field in fields_related.keys():
-                    if field not in self._columns:
+                    if field not in cls._fields:
                         continue
                     for related in fields_related[field]:
-                        if self._columns[field]._type in (
+                        if cls._fields[field]._type in (
                                 'many2one', 'one2one'):
                             if record[field]:
                                 record[field + '.' + related] = (
@@ -693,7 +686,7 @@
                                         record[field]][record['id']][related])
                             else:
                                 record[field + '.' + related] = False
-                        elif self._columns[field]._type == 'reference':
+                        elif cls._fields[field]._type == 'reference':
                             if record[field]:
                                 model_name, record_id = record[field].split(
                                         ',', 1)
@@ -708,44 +701,40 @@
                 for field in to_del:
                     del record[field]
 
-        if int_id:
-            return res[0]
         return res
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, records, values):
         cursor = Transaction().cursor
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
+        ids = map(int, records)
 
         # Call before cursor cache cleaning
-        trigger_eligibles = self.trigger_write_get_eligibles(
-                isinstance(ids, (int, long)) and [ids] or ids)
+        trigger_eligibles = cls.trigger_write_get_eligibles(records)
 
-        super(ModelSQL, self).write(ids, values)
+        super(ModelSQL, cls).write(records, values)
 
-        if not ids:
+        if not records:
             return True
 
-        if self.table_query():
+        if cls.table_query():
             return True
 
         values = values.copy()
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        else:
-            # _update_tree works if only one record has changed
-            update_tree = False
-            for k in self._columns:
-                field = self._columns[k]
-                if isinstance(field, fields.Many2One) \
-                        and field.model_name == self._name \
-                        and field.left and field.right:
-                    update_tree = True
-            if update_tree:
-                for object_id in ids:
-                    self.write(object_id, values)
-                return True
+        # _update_tree works if only one record has changed
+        update_tree = False
+        for k in cls._fields:
+            field = cls._fields[k]
+            if isinstance(field, fields.Many2One) \
+                    and field.model_name == cls.__name__ \
+                    and field.left and field.right:
+                update_tree = True
+        if update_tree and len(records) > 1:
+            for record in records:
+                cls.write([record], values)
+            return True
 
         if Transaction().timestamp:
             for i in range(0, len(ids), cursor.IN_MAX):
@@ -753,25 +742,26 @@
                 clause = ('(id = %s AND '
                         'CAST(EXTRACT(EPOCH FROM '
                         'COALESCE(write_date, create_date)) AS ' + \
-                        FIELDS['numeric'].sql_type(self.create_date)[1] + \
+                        FIELDS['numeric'].sql_type(cls.create_date)[1] + \
                         ') > %s)')
                 args = []
                 for i in sub_ids:
-                    if Transaction().timestamp.get(self._name + ',' + str(i)):
+                    if Transaction().timestamp.get(
+                            cls.__name__ + ',' + str(i)):
                         args.append(i)
                         args.append(Decimal(Transaction().timestamp[
-                            self._name + ',' + str(i)]))
+                            cls.__name__ + ',' + str(i)]))
                 if args:
                     cursor.execute("SELECT id " \
-                            'FROM "' + self._table + '" ' \
+                            'FROM "' + cls._table + '" ' \
                             'WHERE ' + ' OR '.join(
                                 (clause,) * (len(args) // 2)), args)
                     if cursor.fetchone():
                         raise ConcurrencyException(
                             'Records were modified in the meanwhile')
             for i in ids:
-                if Transaction().timestamp.get(self._name + ',' + str(i)):
-                    del Transaction().timestamp[self._name + ',' + str(i)]
+                if Transaction().timestamp.get(cls.__name__ + ',' + str(i)):
+                    del Transaction().timestamp[cls.__name__ + ',' + str(i)]
 
         # Clean values
         for key in ('create_uid', 'create_date', 'write_uid', 'write_date',
@@ -785,38 +775,38 @@
         updend = []
         direct = []
         for field in values:
-            if field in self._columns:
-                if not hasattr(self._columns[field], 'set'):
-                    if ((not getattr(self._columns[field], 'translate', False))
+            if field in cls._fields:
+                if not hasattr(cls._fields[field], 'set'):
+                    if ((not getattr(cls._fields[field], 'translate', False))
                             or (Transaction().language == CONFIG['language'])):
                         upd0.append(('"' + field + '"', '%s'))
-                        upd1.append(FIELDS[self._columns[field]._type]\
+                        upd1.append(FIELDS[cls._fields[field]._type]\
                                 .sql_format(values[field]))
                     direct.append(field)
                 else:
                     upd_todo.append(field)
             else:
                 updend.append(field)
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'selection') \
-                    and self._columns[field].selection \
+            if field in cls._fields \
+                    and hasattr(cls._fields[field], 'selection') \
+                    and cls._fields[field].selection \
                     and values[field]:
-                if self._columns[field]._type == 'reference':
+                if cls._fields[field]._type == 'reference':
                     if isinstance(values[field], basestring):
                         val = values[field].split(',')[0]
                     else:
                         val, _ = values[field]
                 else:
                     val = values[field]
-                if isinstance(self._columns[field].selection, (tuple, list)):
-                    if val not in dict(self._columns[field].selection):
+                if isinstance(cls._fields[field].selection, (tuple, list)):
+                    if val not in dict(cls._fields[field].selection):
                         raise Exception('ValidateError',
                         'The value "%s" for the field "%s" ' \
                                 'is not in the selection' % \
                                 (val, field))
                 else:
-                    if val not in dict(getattr(self,
-                        self._columns[field].selection)()):
+                    if val not in dict(getattr(cls,
+                        cls._fields[field].selection)()):
                         raise Exception('ValidateError',
                         'The value "%s" for the field "%s" ' \
                                 'is not in the selection' % \
@@ -827,7 +817,7 @@
         upd1.append(Transaction().user)
         upd1.append(datetime.datetime.now())
 
-        domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
+        domain1, domain2 = pool.get('ir.rule').domain_get(cls.__name__,
                 mode='write')
         if domain1:
             domain1 = ' AND (' + domain1 + ') '
@@ -835,37 +825,36 @@
             sub_ids = ids[i:i + cursor.IN_MAX]
             red_sql, red_ids = reduce_ids('id', sub_ids)
             if domain1:
-                cursor.execute('SELECT id FROM "' + self._table + '" ' \
+                cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                         'WHERE ' + red_sql + ' ' + domain1,
                         red_ids + domain2)
             else:
-                cursor.execute('SELECT id FROM "' + self._table + '" ' \
+                cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                         'WHERE ' + red_sql, red_ids)
             rowcount = cursor.rowcount
             if rowcount == -1 or rowcount is None:
                 rowcount = len(cursor.fetchall())
             if not rowcount == len({}.fromkeys(sub_ids)):
                 if domain1:
-                    cursor.execute('SELECT id FROM "' + self._table + '" ' \
+                    cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                             'WHERE ' + red_sql, red_ids)
                     rowcount = cursor.rowcount
                     if rowcount == -1 or rowcount is None:
                         rowcount = len(cursor.fetchall())
                     if rowcount == len({}.fromkeys(sub_ids)):
-                        self.raise_user_error('access_error',
-                                self._description)
-                self.raise_user_error('write_error', self._description)
+                        cls.raise_user_error('access_error', cls.__name__)
+                cls.raise_user_error('write_error', cls.__name__)
             try:
-                cursor.execute('UPDATE "' + self._table + '" '
+                cursor.execute('UPDATE "' + cls._table + '" '
                     'SET ' + ','.join([x[0] + ' = ' + x[1] for x in upd0])
                     + ' WHERE ' + red_sql, upd1 + red_ids)
             except DatabaseIntegrityError, exception:
                 with contextlib.nested(Transaction().new_cursor(),
                         Transaction().set_user(0)):
                     for field_name in values:
-                        if field_name not in self._columns:
+                        if field_name not in cls._fields:
                             continue
-                        field = self._columns[field_name]
+                        field = cls._fields[field_name]
                         # Check required fields
                         if field.required and \
                                 not hasattr(field, 'set') and \
@@ -874,17 +863,17 @@
                                 field_name not in ('create_uid',
                                     'create_date'):
                             if not values[field_name]:
-                                self.raise_user_error('required_field',
-                                        error_args=self._get_error_args(
+                                cls.raise_user_error('required_field',
+                                        error_args=cls._get_error_args(
                                             field_name))
                         if isinstance(field, fields.Many2One) \
                                 and values[field_name]:
-                            model_obj = pool.get(field.model_name)
+                            Model = pool.get(field.model_name)
                             create_records = Transaction().create_records.get(
                                     field.model_name, set())
                             delete_records = Transaction().delete_records.get(
                                     field.model_name, set())
-                            if not ((model_obj.search([
+                            if not ((Model.search([
                                                 ('id', '=',
                                                     values[field_name]),
                                                 ], order=[])
@@ -892,92 +881,93 @@
                                             in create_records))
                                     and (values[field_name]
                                         not in delete_records)):
-                                self.raise_user_error('foreign_model_missing',
-                                    error_args=self._get_error_args(
+                                cls.raise_user_error('foreign_model_missing',
+                                    error_args=cls._get_error_args(
                                         field_name))
-                    for name, _, error in self._sql_constraints:
+                    for name, _, error in cls._sql_constraints:
                         if name in exception[0]:
-                            self.raise_user_error(error)
-                    for name, error in self._sql_error_messages:
+                            cls.raise_user_error(error)
+                    for name, error in cls._sql_error_messages:
                         if name in exception[0]:
-                            self.raise_user_error(error)
+                            cls.raise_user_error(error)
                 raise
 
         for field in direct:
-            if getattr(self._columns[field], 'translate', False):
-                translation_obj.set_ids(
-                        self._name + ',' + field, 'model',
+            if getattr(cls._fields[field], 'translate', False):
+                Translation.set_ids(
+                        cls.__name__ + ',' + field, 'model',
                         Transaction().language, ids, values[field])
 
         # call the 'set' method of fields
         for field in upd_todo:
-            self._columns[field].set(ids, self, field, values[field])
+            cls._fields[field].set(ids, cls, field, values[field])
 
-        if self._history:
-            columns = ['"' + str(x) + '"' for x in self._columns
-                    if not hasattr(self._columns[x], 'set')]
+        if cls._history:
+            columns = ['"' + str(x) + '"' for x in cls._fields
+                    if not hasattr(cls._fields[x], 'set')]
             for obj_id in ids:
-                cursor.execute('INSERT INTO "' + self._table + '__history" ' \
+                cursor.execute('INSERT INTO "' + cls._table + '__history" ' \
                         '(' + ','.join(columns) + ') ' \
                         'SELECT ' + ','.join(columns) + ' ' + \
-                        'FROM "' + self._table + '" ' \
+                        'FROM "' + cls._table + '" ' \
                         'WHERE id = %s', (obj_id,))
 
-        for table in self._inherits:
-            col = self._inherits[table]
-            nids = []
+        for iname in cls._inherits:
+            col = cls._inherits[iname]
+            iids = []
             for i in range(0, len(ids), cursor.IN_MAX):
                 sub_ids = ids[i:i + cursor.IN_MAX]
                 red_sql, red_ids = reduce_ids('id', sub_ids)
                 cursor.execute('SELECT DISTINCT "' + col + '" ' \
-                        'FROM "' + self._table + '" WHERE ' + red_sql,
+                        'FROM "' + cls._table + '" WHERE ' + red_sql,
                         red_ids)
-                nids.extend([x[0] for x in cursor.fetchall()])
+                iids.extend([x[0] for x in cursor.fetchall()])
 
             values2 = {}
             for val in updend:
-                if self._inherit_fields[val][0] == table:
+                if cls._inherit_fields[val][0] == iname:
                     values2[val] = values[val]
             with Transaction().set_user(0, set_context=True):
-                pool.get(table).write(nids, values2)
+                IModel = pool.get(iname)
+                IModel.write(IModel.browse(iids), values2)
 
-        self._validate(ids)
+        cls._validate(records)
 
         # Check for Modified Preorder Tree Traversal
-        for k in self._columns:
-            field = self._columns[k]
+        for k in cls._fields:
+            field = cls._fields[k]
             if isinstance(field, fields.Many2One) \
-                    and field.model_name == self._name \
+                    and field.model_name == cls.__name__ \
                     and field.left and field.right:
                 if field.left in values or field.right in values:
                     raise Exception('ValidateError',
                             'You can not update fields: "%s", "%s"' %
                             (field.left, field.right))
                 if len(ids) == 1:
-                    self._update_tree(ids[0], k, field.left, field.right)
+                    cls._update_tree(ids[0], k, field.left, field.right)
                 else:
                     with Transaction().set_user(0):
-                        self._rebuild_tree(k, False, 0)
+                        cls._rebuild_tree(k, False, 0)
 
-        self.trigger_write(trigger_eligibles)
+        cls.trigger_write(trigger_eligibles)
 
         return True
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, records):
         cursor = Transaction().cursor
         pool = Pool()
+        ids = map(int, records)
+
         if not ids:
             return True
 
-        if self.table_query():
+        if cls.table_query():
             return True
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        if Transaction().delete and Transaction().delete.get(self._name):
+        if Transaction().delete and Transaction().delete.get(cls.__name__):
             ids = ids[:]
-            for del_id in Transaction().delete[self._name]:
+            for del_id in Transaction().delete[cls.__name__]:
                 for i in range(ids.count(del_id)):
                     ids.remove(del_id)
 
@@ -987,34 +977,35 @@
                 clause = ('(id = %s AND '
                         'CAST(EXTRACT(EPOCH FROM '
                         'COALESCE(write_date, create_date)) AS ' + \
-                        FIELDS['numeric'].sql_type(self.create_date)[1] + \
+                        FIELDS['numeric'].sql_type(cls.create_date)[1] + \
                         ') > %s)')
                 args = []
                 for i in sub_ids:
-                    if Transaction().timestamp.get(self._name + ',' + str(i)):
+                    if Transaction().timestamp.get(
+                            cls.__name__ + ',' + str(i)):
                         args.append(i)
                         args.append(Transaction().timestamp[
-                            self._name + ',' + str(i)])
+                            cls.__name__ + ',' + str(i)])
                 if args:
                     cursor.execute("SELECT id "
-                        'FROM "' + self._table + '" '
+                        'FROM "' + cls._table + '" '
                         'WHERE ' + ' OR '.join(
                             (clause,) * (len(args) / 2)), args)
                     if cursor.fetchone():
                         raise ConcurrencyException(
                             'Records were modified in the meanwhile')
             for i in ids:
-                if Transaction().timestamp.get(self._name + ',' + str(i)):
-                    del Transaction().timestamp[self._name + ',' + str(i)]
+                if Transaction().timestamp.get(cls.__name__ + ',' + str(i)):
+                    del Transaction().timestamp[cls.__name__ + ',' + str(i)]
 
         tree_ids = {}
-        for k in self._columns:
-            field = self._columns[k]
+        for k in cls._fields:
+            field = cls._fields[k]
             if isinstance(field, fields.Many2One) \
-                    and field.model_name == self._name \
+                    and field.model_name == cls.__name__ \
                     and field.left and field.right:
                 red_sql, red_ids = reduce_ids('"' + k + '"', ids)
-                cursor.execute('SELECT id FROM "' + self._table + '" '
+                cursor.execute('SELECT id FROM "' + cls._table + '" '
                     'WHERE ' + red_sql, red_ids)
                 tree_ids[k] = [x[0] for x in cursor.fetchall()]
 
@@ -1025,11 +1016,11 @@
             if hasattr(model, 'table_query') \
                     and model.table_query():
                 continue
-            if not isinstance(model, ModelStorage):
+            if not issubclass(model, ModelStorage):
                 continue
-            for field_name, field in model._columns.iteritems():
+            for field_name, field in model._fields.iteritems():
                 if isinstance(field, fields.Many2One) \
-                        and field.model_name == self._name:
+                        and field.model_name == cls.__name__:
                     if field.ondelete == 'CASCADE':
                         foreign_keys_todelete.append((model, field_name))
                     elif field.ondelete == 'SET NULL':
@@ -1040,9 +1031,9 @@
                     else:
                         foreign_keys_tocheck.append((model, field_name))
 
-        Transaction().delete.setdefault(self._name, set()).update(ids)
+        Transaction().delete.setdefault(cls.__name__, set()).update(ids)
 
-        domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
+        domain1, domain2 = pool.get('ir.rule').domain_get(cls.__name__,
                 mode='delete')
         if domain1:
             domain1 = ' AND (' + domain1 + ') '
@@ -1051,110 +1042,110 @@
             sub_ids = ids[i:i + cursor.IN_MAX]
             red_sql, red_ids = reduce_ids('id', sub_ids)
             if domain1:
-                cursor.execute('SELECT id FROM "' + self._table + '" '
+                cursor.execute('SELECT id FROM "' + cls._table + '" '
                     'WHERE ' + red_sql + ' ' + domain1,
                     red_ids + domain2)
                 rowcount = cursor.rowcount
                 if rowcount == -1 or rowcount is None:
                     rowcount = len(cursor.fetchall())
                 if not rowcount == len({}.fromkeys(sub_ids)):
-                    self.raise_user_error('access_error', self._description)
+                    cls.raise_user_error('access_error', cls._description)
 
-        self.trigger_delete(ids)
+        cls.trigger_delete(records)
 
         for i in range(0, len(ids), cursor.IN_MAX):
             sub_ids = ids[i:i + cursor.IN_MAX]
+            sub_records = records[i:i + cursor.IN_MAX]
             red_sql, red_ids = reduce_ids('id', sub_ids)
 
-            Transaction().delete_records.setdefault(self._name,
+            Transaction().delete_records.setdefault(cls.__name__,
                     set()).update(sub_ids)
 
-            for model, field_name in foreign_keys_toupdate:
-                if not hasattr(model, 'search') \
-                        or not hasattr(model, 'write'):
+            for Model, field_name in foreign_keys_toupdate:
+                if (not hasattr(Model, 'search')
+                        or not hasattr(Model, 'write')):
                     continue
                 red_sql2, red_ids2 = reduce_ids('"' + field_name + '"',
                     sub_ids)
-                cursor.execute('SELECT id FROM "' + model._table + '" '
+                cursor.execute('SELECT id FROM "' + Model._table + '" '
                     'WHERE ' + red_sql2, red_ids2)
-                model_ids = [x[0] for x in cursor.fetchall()]
-                if model_ids:
-                    model.write(model_ids, {
-                        field_name: False,
-                        })
+                models = Model.browse([x[0] for x in cursor.fetchall()])
+                if models:
+                    Model.write(models, {
+                            field_name: False,
+                            })
 
-            for model, field_name in foreign_keys_todelete:
-                if not hasattr(model, 'search') \
-                        or not hasattr(model, 'delete'):
+            for Model, field_name in foreign_keys_todelete:
+                if (not hasattr(Model, 'search')
+                        or not hasattr(Model, 'delete')):
                     continue
                 red_sql2, red_ids2 = reduce_ids('"' + field_name + '"',
                     sub_ids)
-                cursor.execute('SELECT id FROM "' + model._table + '" '
+                cursor.execute('SELECT id FROM "' + Model._table + '" '
                     'WHERE ' + red_sql2, red_ids2)
-                model_ids = [x[0] for x in cursor.fetchall()]
-                if model_ids:
-                    model.delete(model_ids)
+                models = Model.browse([x[0] for x in cursor.fetchall()])
+                if models:
+                    Model.delete(models)
 
-            for model, field_name in foreign_keys_tocheck:
+            for Model, field_name in foreign_keys_tocheck:
                 with Transaction().set_user(0):
-                    if model.search([
-                        (field_name, 'in', sub_ids),
-                        ], order=[]):
+                    if Model.search([
+                                (field_name, 'in', sub_ids),
+                                ], order=[]):
                         error_args = []
-                        error_args.append(self._get_error_args('id')[1])
+                        error_args.append(cls._get_error_args('id')[1])
                         error_args.extend(list(
-                            model._get_error_args(field_name)))
-                        self.raise_user_error('foreign_model_exist',
-                                error_args=tuple(error_args))
+                                Model._get_error_args(field_name)))
+                        cls.raise_user_error('foreign_model_exist',
+                            error_args=tuple(error_args))
 
-            super(ModelSQL, self).delete(sub_ids)
+            super(ModelSQL, cls).delete(sub_records)
 
             try:
-                cursor.execute('DELETE FROM "' + self._table + '" '
+                cursor.execute('DELETE FROM "' + cls._table + '" '
                     'WHERE ' + red_sql, red_ids)
             except DatabaseIntegrityError, exception:
                 with Transaction().new_cursor():
-                    for name, _, error in self._sql_constraints:
+                    for name, _, error in cls._sql_constraints:
                         if name in exception[0]:
-                            self.raise_user_error(error)
-                    for name, error in self._sql_error_messages:
+                            cls.raise_user_error(error)
+                    for name, error in cls._sql_error_messages:
                         if name in exception[0]:
-                            self.raise_user_error(error)
+                            cls.raise_user_error(error)
                 raise
 
-        if self._history:
+        if cls._history:
             for obj_id in ids:
-                cursor.execute('INSERT INTO "' + self._table + '__history" ' \
+                cursor.execute('INSERT INTO "' + cls._table + '__history" ' \
                         '(id, write_uid, write_date) VALUES (%s, %s, %s)',
                         (obj_id, Transaction().user, datetime.datetime.now()))
 
         for k in tree_ids.keys():
-            field = self._columns[k]
+            field = cls._fields[k]
             if len(tree_ids[k]) == 1:
-                self._update_tree(tree_ids[k][0], k, field.left, field.right)
+                cls._update_tree(tree_ids[k][0], k, field.left, field.right)
             else:
                 with Transaction().set_user(0):
-                    self._rebuild_tree(k, False, 0)
+                    cls._rebuild_tree(k, False, 0)
 
-        return True
-
-    def search(self, domain, offset=0, limit=None, order=None, count=False,
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False,
             query_string=False):
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
+        Rule = pool.get('ir.rule')
         cursor = Transaction().cursor
 
         # Get domain clauses
-        qu1, qu2, tables, tables_args = self.search_domain(domain)
+        qu1, qu2, tables, tables_args = cls.search_domain(domain)
 
         # Get order by
         order_by = []
         if order is None or order is False:
-            order = self._order
+            order = cls._order
         for field, otype in order:
             if otype.upper() not in ('DESC', 'ASC'):
                 raise Exception('Error', 'Wrong order type (%s)!' % otype)
-            order_by2, tables2, tables2_args = self._order_calc(field, otype)
+            order_by2, tables2, tables2_args = cls._order_calc(field, otype)
             order_by += order_by2
             for table in tables2:
                 if table not in tables:
@@ -1170,7 +1161,7 @@
             raise Exception('Error', 'Wrong offset type (%s)!' % type(offset))
 
         # construct a clause for the rules :
-        domain1, domain2 = rule_obj.domain_get(self._name, mode='read')
+        domain1, domain2 = Rule.domain_get(cls.__name__, mode='read')
         if domain1:
             if qu1:
                 qu1 += ' AND ' + domain1
@@ -1180,29 +1171,29 @@
 
         if count:
             cursor.execute(cursor.limit_clause(
-                'SELECT COUNT("%s".id) FROM ' % self._table +
+                'SELECT COUNT("%s".id) FROM ' % cls._table +
                     ' '.join(tables) + (qu1 and ' WHERE ' + qu1 or ''),
                     limit, offset), tables_args + qu2)
             res = cursor.fetchall()
             return res[0][0]
         # execute the "main" query to fetch the ids we were searching for
-        select_fields = ['"' + self._table + '".id AS id']
-        if self._history and Transaction().context.get('_datetime') \
+        select_fields = ['"' + cls._table + '".id AS id']
+        if cls._history and Transaction().context.get('_datetime') \
                 and not query_string:
-            select_fields += ['COALESCE("' + self._table + '".write_date, "'
-                + self._table + '".create_date) AS _datetime']
+            select_fields += ['COALESCE("' + cls._table + '".write_date, "'
+                + cls._table + '".create_date) AS _datetime']
         if not query_string:
             select_fields += [
-                '"' + self._table + '"."' + name + '" AS "' + name + '"'
-                for name, field in self._columns.iteritems()
+                '"' + cls._table + '"."' + name + '" AS "' + name + '"'
+                for name, field in cls._fields.iteritems()
                 if not hasattr(field, 'get')
                 and name != 'id'
                 and not getattr(field, 'translate', False)
                 and field.loading == 'eager']
-            if not self.table_query():
+            if not cls.table_query():
                 select_fields += ['CAST(EXTRACT(EPOCH FROM '
-                        '(COALESCE("' + self._table + '".write_date, '
-                        '"' + self._table + '".create_date))) AS VARCHAR'
+                        '(COALESCE("' + cls._table + '".write_date, '
+                        '"' + cls._table + '".create_date))) AS VARCHAR'
                         ') AS _timestamp']
         query_str = cursor.limit_clause(
                 'SELECT ' + ','.join(select_fields) + ' FROM ' + \
@@ -1212,13 +1203,13 @@
             return (query_str, tables_args + qu2)
         cursor.execute(query_str, tables_args + qu2)
 
-        datas = cursor.dictfetchmany(cursor.IN_MAX)
+        rows = cursor.dictfetchmany(cursor.IN_MAX)
         cache = cursor.get_cache()
-        cache.setdefault(self._name, LRUDict(RECORD_CACHE_SIZE))
-        delete_records = Transaction().delete_records.setdefault(self._name,
+        cache.setdefault(cls.__name__, LRUDict(RECORD_CACHE_SIZE))
+        delete_records = Transaction().delete_records.setdefault(cls.__name__,
                 set())
         keys = None
-        for data in islice(datas, 0, cache.size_limit):
+        for data in islice(rows, 0, cache.size_limit):
             if data['id'] in delete_records:
                 continue
             if not keys:
@@ -1227,17 +1218,17 @@
                     if k in ('_timestamp', '_datetime'):
                         keys.remove(k)
                         continue
-                    field = self._columns[k]
+                    field = cls._fields[k]
                     if field._type not in ('many2one',):
                         keys.remove(k)
                         continue
             for k in keys:
                 del data[k]
-            cache[self._name].setdefault(data['id'], {}).update(data)
+            cache[cls.__name__].setdefault(data['id'], {}).update(data)
 
-        if len(datas) >= cursor.IN_MAX:
+        if len(rows) >= cursor.IN_MAX:
             select_fields2 = [select_fields[0]]
-            if (self._history
+            if (cls._history
                     and Transaction().context.get('_datetime')
                     and not query_string):
                 select_fields2 += [select_fields[1]]
@@ -1247,14 +1238,14 @@
                         'SELECT ' + ','.join(select_fields2) + ' FROM ' + \
                         ' '.join(tables) + (qu1 and ' WHERE ' + qu1 or '') + \
                         (order_by and ' ORDER BY ' + order_by or ''),
-                        limit, offset) + ') AS "' + self._table + '"',
+                        limit, offset) + ') AS "' + cls._table + '"',
                     tables_args + qu2)
-            datas = cursor.dictfetchall()
+            rows = cursor.dictfetchall()
 
-        if self._history and Transaction().context.get('_datetime'):
+        if cls._history and Transaction().context.get('_datetime'):
             res = []
             ids_date = {}
-            for data in datas:
+            for data in rows:
                 if data['id'] in ids_date:
                     if data['_datetime'] <= ids_date[data['id']]:
                         continue
@@ -1269,50 +1260,45 @@
                 cursor.execute(('SELECT id, write_date '
                         'FROM "%s__history" WHERE %s'
                         'AND (write_date IS NOT NULL AND create_date IS NULL)'
-                        ) % (self._table, reduced_sql),
+                        ) % (cls._table, reduced_sql),
                     reduced_ids)
                 for deleted_id, delete_date in cursor.fetchall():
                     if ids_date[deleted_id] < delete_date:
                         to_delete.add(deleted_id)
-            return filter(lambda x: x not in to_delete, res)
+            return cls.browse(filter(lambda x: x not in to_delete, res))
 
-        return [x['id'] for x in datas]
+        return cls.browse([x['id'] for x in rows])
 
-    def search_domain(self, domain, active_test=True):
+    @classmethod
+    def search_domain(cls, domain, active_test=True):
         '''
-        Return SQL clause and arguments for the domain
-
-        :param domain: a domain like in search
-        :param active_test: a boolean to add 'active' test
-        :return: a tuple with
-            - a SQL clause string
-            - a list of arguments for the SQL clause
-            - a list of tables used in the SQL clause
-            - a list of arguments for the tables
+        Return SQL clause, tables to use and arguments for the domain
+        Set active_test to add it.
         '''
-        domain = self._search_domain_active(domain, active_test=active_test)
+        domain = cls._search_domain_active(domain, active_test=active_test)
 
         table_query = ''
         tables_args = []
-        if self.table_query():
-            table_query, tables_args = self.table_query()
+        if cls.table_query():
+            table_query, tables_args = cls.table_query()
             table_query = '(' + table_query + ') AS '
 
-        if self._history and Transaction().context.get('_datetime'):
-            table_query = '"' + self._table + '__history" AS '
+        if cls._history and Transaction().context.get('_datetime'):
+            table_query = '"' + cls._table + '__history" AS '
 
-        tables = [table_query + '"' + self._table + '"']
+        tables = [table_query + '"' + cls._table + '"']
 
-        qu1, qu2 = self.__search_domain_oper(domain, tables, tables_args)
-        if self._history and Transaction().context.get('_datetime'):
+        qu1, qu2 = cls.__search_domain_oper(domain, tables, tables_args)
+        if cls._history and Transaction().context.get('_datetime'):
             if qu1:
                 qu1 += ' AND'
-            qu1 += ' (COALESCE("' + self._table + '".write_date, "' + \
-                    self._table + '".create_date) <= %s)'
+            qu1 += ' (COALESCE("' + cls._table + '".write_date, "' + \
+                    cls._table + '".create_date) <= %s)'
             qu2 += [Transaction().context['_datetime']]
         return qu1, qu2, tables, tables_args
 
-    def __search_domain_oper(self, domain, tables, tables_args):
+    @classmethod
+    def __search_domain_oper(cls, domain, tables, tables_args):
         operator = 'AND'
         if len(domain) and isinstance(domain[0], basestring):
             if domain[0] not in ('AND', 'OR'):
@@ -1331,14 +1317,14 @@
             elif isinstance(arg, list):
                 list_args.append(arg)
 
-        qu1, qu2 = self.__search_domain_calc(tuple_args, tables, tables_args)
+        qu1, qu2 = cls.__search_domain_calc(tuple_args, tables, tables_args)
         if len(qu1):
             qu1 = (' ' + operator + ' ').join(qu1)
         else:
             qu1 = ''
 
         for domain2 in list_args:
-            qu1b, qu2b = self.__search_domain_oper(domain2, tables,
+            qu1b, qu2b = cls.__search_domain_oper(domain2, tables,
                     tables_args)
             if not qu1b:
                 qu1b = '%s'
@@ -1352,30 +1338,31 @@
             qu1 = '(' + qu1 + ')'
         return qu1, qu2
 
-    def __search_domain_calc(self, domain, tables, tables_args):
+    @classmethod
+    def __search_domain_calc(cls, domain, tables, tables_args):
         pool = Pool()
         domain = domain[:]
         cursor = Transaction().cursor
 
         def column_get(model, field_name):
-            field = model._columns.get(field_name)
+            field = model._fields.get(field_name)
             if not field:
                 if not field_name in model._inherit_fields:
                     raise Exception('ValidateError', 'Field "%s" doesn\'t ' \
-                            'exist on "%s"' % (field_name, model._name))
+                            'exist on "%s"' % (field_name, model.__name__))
                 parent_model = pool.get(model._inherit_fields[field_name][0])
                 field = column_get(parent_model, field_name)
             return field
 
         def get_model_for_field(model, field):
-            if field in model._columns:
+            if field in model._fields:
                 return model
             else:
                 parent_model = pool.get(model._inherit_fields[field][0])
                 return get_model_for_field(parent_model, field)
 
         def get_inherits_join(model, field):
-            if field in model._columns:
+            if field in model._fields:
                 return []
             itable = pool.get(model._inherit_fields[field][0])
             table_query = ''
@@ -1386,7 +1373,7 @@
             table_join = 'LEFT JOIN ' + table_query + \
                     '"' + itable._table + '" ON ' \
                     '"%s".id = "%s"."%s"' % (itable._table, model._table,
-                            model._inherits[itable._name])
+                            model._inherits[itable.__name__])
             inherits = [(table_join, table_arg)]
             inherits.extend(get_inherits_join(itable, field))
             return inherits
@@ -1399,88 +1386,91 @@
         joins = []
         while i < len(domain):
             fargs = domain[i][0].split('.', 1)
-            if fargs[0] in self._inherit_fields:
-                inherit_joins = get_inherits_join(self, fargs[0])
+            if fargs[0] in cls._inherit_fields:
+                inherit_joins = get_inherits_join(cls, fargs[0])
                 for (table_join, table_arg) in inherit_joins:
                     if table_join not in tables:
                         tables.append(table_join)
                         tables_args.extend(table_arg)
 
-            field = column_get(self, fargs[0])
-            table = get_model_for_field(self, fargs[0])
+            field = column_get(cls, fargs[0])
+            table = get_model_for_field(cls, fargs[0])
             if len(fargs) > 1:
                 if field._type == 'many2one':
-                    target_obj = pool.get(field.model_name)
+                    Target = pool.get(field.model_name)
                     m2o_search = [(fargs[1], domain[i][1], domain[i][2])]
-                    if ('active' in target_obj._columns
-                            or 'active' in target_obj._inherit_fields):
+                    if ('active' in Target._fields
+                            or 'active' in Target._inherit_fields):
                         m2o_search += [('active', 'in', (True, False))]
                     if hasattr(field, 'search'):
                         domain.extend([(fargs[0], 'in',
-                                    target_obj.search(m2o_search, order=[]))])
+                                    map(int, Target.search(m2o_search,
+                                            order=[])))])
                         domain.pop(i)
                     else:
                         domain[i] = (fargs[0], 'inselect',
-                            target_obj.search(m2o_search, order=[],
+                            Target.search(m2o_search, order=[],
                                 query_string=True), table)
                         i += 1
                     continue
                 elif field._type in ('one2one', 'many2many', 'one2many'):
                     if hasattr(field, 'model_name'):
-                        target_obj = pool.get(field.model_name)
+                        Target = pool.get(field.model_name)
                     else:
-                        target_obj = field.get_target()
+                        Target = field.get_target()
                     if hasattr(field, 'relation_name'):
-                        relation_obj = pool.get(field.relation_name)
+                        Relation = pool.get(field.relation_name)
                         origin, target = field.origin, field.target
                     else:
-                        relation_obj = target_obj
+                        Relation = Target
                         origin, target = field.field, 'id'
-                    if origin in relation_obj._columns:
-                        rev_field = relation_obj._columns[origin]
+                    if origin in Relation._fields:
+                        rev_field = Relation._fields[origin]
                     else:
-                        rev_field = relation_obj._inherit_fields[origin][2]
+                        rev_field = Relation._inherit_fields[origin][2]
                     if rev_field._type == 'reference':
                         sql_type = FIELDS[
-                            self.id._type].sql_type(self.id)[0]
+                            cls.id._type].sql_type(cls.id)[0]
                         origin = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)'
                             % (origin, sql_type))
                     else:
                         origin = '"%s"' % origin
                     if hasattr(field, 'search'):
-                        domain.extend([(fargs[0], 'in', target_obj.search([
-                            (fargs[1], domain[i][1], domain[i][2]),
-                            ], order=[]))])
+                        domain.extend([(fargs[0], 'in',
+                                    map(int, Target.search([
+                                                (fargs[1], domain[i][1],
+                                                    domain[i][2]),
+                                                ], order=[])))])
                         domain.pop(i)
                     else:
-                        query1, query2 = target_obj.search([
+                        query1, query2 = Target.search([
                             (fargs[1], domain[i][1], domain[i][2]),
                             ], order=[], query_string=True)
                         query1 = ('SELECT %s FROM "%s" WHERE "%s" IN (%s)' %
-                                (origin, relation_obj._table, target, query1))
+                                (origin, Relation._table, target, query1))
                         domain[i] = ('id', 'inselect', (query1, query2))
                         i += 1
                     continue
                 else:
                     raise Exception('ValidateError', 'Clause on field "%s" ' \
                             'doesn\'t work on "%s"' %
-                            (domain[i][0], self._name))
+                            (domain[i][0], cls.__name__))
             if hasattr(field, 'search'):
                 clause = domain.pop(i)
                 domain.extend(field.search(table, clause[0], clause))
             elif field._type == 'one2many':
-                field_obj = pool.get(field.model_name)
+                Field = pool.get(field.model_name)
                 table_query = ''
                 table_args = []
-                if field_obj.table_query():
-                    table_query, table_args = field_obj.table_query()
+                if Field.table_query():
+                    table_query, table_args = Field.table_query()
                     table_query = '(' + table_query + ') AS '
-                if field.field in field_obj._columns:
-                    rev_field = field_obj._columns[field.field]
+                if field.field in Field._fields:
+                    rev_field = Field._fields[field.field]
                 else:
-                    rev_field = field_obj._inherit_fields[field.field][2]
+                    rev_field = Field._inherit_fields[field.field][2]
                 if rev_field._type == 'reference':
-                    sql_type = FIELDS[self.id._type].sql_type(self.id)[0]
+                    sql_type = FIELDS[cls.id._type].sql_type(cls.id)[0]
                     select = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)'
                         % (field.field, sql_type))
                 else:
@@ -1489,7 +1479,7 @@
                 if isinstance(domain[i][2], bool) or domain[i][2] is None:
                     query1 = 'SELECT ' + select + ' ' \
                             'FROM ' + table_query + \
-                                '"' + field_obj._table + '" ' \
+                                '"' + Field._table + '" ' \
                             'WHERE "' + field.field + '" IS NOT NULL'
                     query2 = table_args
                     clause = 'inselect'
@@ -1501,12 +1491,12 @@
                         target_field = 'rec_name'
                     else:
                         target_field = 'id'
-                    query1, query2 = field_obj.search([
+                    query1, query2 = Field.search([
                             (target_field, domain[i][1], domain[i][2]),
                             ], order=[], query_string=True)
                     query1 = ('SELECT ' + select + ' '
                         'FROM ' + table_query +
-                            '"' + field_obj._table + '" '
+                            '"' + Field._table + '" '
                         'WHERE id IN (' + query1 + ')')
                     query2 = table_args + query2
                     domain[i] = ('id', 'inselect', (query1, query2))
@@ -1514,14 +1504,14 @@
             elif field._type in ('many2many', 'one2one'):
                 # XXX must find a solution for long id list
                 if hasattr(field, 'model_name'):
-                    target_obj = pool.get(field.model_name)
+                    Target = pool.get(field.model_name)
                 else:
-                    target_obj = field.get_target()
+                    Target = field.get_target()
                 if domain[i][1] in ('child_of', 'not child_of'):
                     if isinstance(domain[i][2], basestring):
-                        ids2 = [x[0] for x in target_obj.search([
-                            ('rec_name', 'ilike', domain[i][2]),
-                            ], order=[])]
+                        ids2 = map(int, Target.search([
+                                    ('rec_name', 'ilike', domain[i][2]),
+                                    ], order=[]))
                     elif isinstance(domain[i][2], (int, long)):
                         ids2 = [domain[i][2]]
                     else:
@@ -1530,25 +1520,25 @@
                     def _rec_get(ids, table, parent):
                         if not ids:
                             return []
-                        ids2 = table.search([
-                            (parent, 'in', ids),
-                            (parent, '!=', False),
-                            ], order=[])
+                        ids2 = map(int, table.search([
+                                    (parent, 'in', ids),
+                                    (parent, '!=', None),
+                                    ], order=[]))
                         return ids + _rec_get(ids2, table, parent)
 
-                    if target_obj._name != table._name:
+                    if Target.__name__ != table.__name__:
                         if len(domain[i]) != 4:
                             raise Exception('Error', 'Programming error: '
                                 'child_of on field "%s" is not allowed!'
                                 % domain[i][0])
-                        ids2 = target_obj.search([
-                            (domain[i][3], 'child_of', ids2),
-                            ], order=[])
-                        relation_obj = pool.get(field.relation_name)
+                        ids2 = map(int, Target.search([
+                                    (domain[i][3], 'child_of', ids2),
+                                    ], order=[]))
+                        Relation = pool.get(field.relation_name)
                         red_sql, red_ids = reduce_ids('"' + field.target + '"',
                                 ids2)
                         query1 = 'SELECT "' + field.origin + '" ' \
-                                'FROM "' + relation_obj._table + '" ' \
+                                'FROM "' + Relation._table + '" ' \
                                 'WHERE ' + red_sql + ' ' \
                                     'AND "' + field.origin + '" IS NOT NULL'
                         query2 = red_ids
@@ -1564,19 +1554,19 @@
                             domain[i] = ('id', 'not in', ids2 + _rec_get(ids2,
                                 table, domain[i][0]))
                 else:
-                    relation_obj = pool.get(field.relation_name)
+                    Relation = pool.get(field.relation_name)
                     table_query = ''
                     table_args = []
-                    if relation_obj.table_query():
-                        table_query, table_args = relation_obj.table_query()
+                    if Relation.table_query():
+                        table_query, table_args = Relation.table_query()
                         table_query = '(' + table_query + ') AS '
-                    if field.origin in relation_obj._columns:
-                        origin_field = relation_obj._columns[field.origin]
+                    if field.origin in Relation._fields:
+                        origin_field = Relation._fields[field.origin]
                     else:
                         origin_field = \
-                            relation_obj._inherit_fields[field.origin][2]
+                            Relation._inherit_fields[field.origin][2]
                     if origin_field._type == 'reference':
-                        sql_type = FIELDS[self.id._type].sql_type(self.id)[0]
+                        sql_type = FIELDS[cls.id._type].sql_type(cls.id)[0]
                         select = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)'
                             % (field.origin, sql_type))
                     else:
@@ -1584,7 +1574,7 @@
                     if isinstance(domain[i][2], bool) or domain[i][2] is None:
                         query1 = 'SELECT ' + select + ' ' \
                                 'FROM ' + table_query + ' '\
-                                    '"' + relation_obj._table + '" ' \
+                                    '"' + Relation._table + '" ' \
                                 'WHERE "' + field.origin + '" IS NOT NULL'
                         query2 = table_args
                         clause = 'inselect'
@@ -1596,14 +1586,13 @@
                             target_field = 'rec_name'
                         else:
                             target_field = 'id'
-                        relation_obj = pool.get(field.relation_name)
 
-                        query1, query2 = target_obj.search([
+                        query1, query2 = Target.search([
                                     (target_field, domain[i][1], domain[i][2]),
                                     ], order=[], query_string=True)
                         query1 = ('SELECT ' + select + ' '
                             'FROM ' + table_query +
-                                '"' + relation_obj._table + '" '
+                                '"' + Relation._table + '" '
                             'WHERE "' + field.target + '" IN (' + query1 + ')')
                         query2 = table_args + query2
                         domain[i] = ('id', 'inselect', (query1, query2))
@@ -1613,10 +1602,10 @@
                 # XXX must find a solution for long id list
                 if domain[i][1] in ('child_of', 'not child_of'):
                     if isinstance(domain[i][2], basestring):
-                        field_obj = pool.get(field.model_name)
-                        ids2 = [x[0] for x in field_obj.search([
-                            ('rec_name', 'like', domain[i][2]),
-                            ], order=[])]
+                        Field = pool.get(field.model_name)
+                        ids2 = map(int, Field.search([
+                                    ('rec_name', 'like', domain[i][2]),
+                                    ], order=[]))
                     elif isinstance(domain[i][2], (int, long)):
                         ids2 = [domain[i][2]]
                     else:
@@ -1625,20 +1614,20 @@
                     def _rec_get(ids, table, parent):
                         if not ids:
                             return []
-                        ids2 = table.search([
-                            (parent, 'in', ids),
-                            (parent, '!=', False),
-                            ])
+                        ids2 = map(int, table.search([
+                                    (parent, 'in', ids),
+                                    (parent, '!=', None),
+                                    ]))
                         return ids + _rec_get(ids2, table, parent)
 
-                    if field.model_name != table._name:
+                    if field.model_name != table.__name__:
                         if len(domain[i]) != 4:
                             raise Exception('Error', 'Programming error: '
                                 'child_of on field "%s" is not allowed!'
                                 % domain[i][0])
-                        ids2 = pool.get(field.model_name).search([
-                            (domain[i][3], 'child_of', ids2),
-                            ], order=[])
+                        ids2 = map(int, pool.get(field.model_name).search([
+                                    (domain[i][3], 'child_of', ids2),
+                                    ], order=[]))
                         if domain[i][1] == 'child_of':
                             domain[i] = (domain[i][0], 'in', ids2, table)
                         else:
@@ -1648,7 +1637,7 @@
                             red_sql, red_ids = reduce_ids('id', ids2)
                             cursor.execute('SELECT "' + field.left + '", ' \
                                         '"' + field.right + '" ' + \
-                                    'FROM "' + self._table + '" ' + \
+                                    'FROM "' + cls._table + '" ' + \
                                     'WHERE ' + red_sql, red_ids)
                             clause = '(1=0) '
                             for left, right in cursor.fetchall():
@@ -1658,7 +1647,7 @@
                                         'AND "' + field.right + '" <= ' + \
                                         str(right) + ')'
 
-                            query = 'SELECT id FROM "' + self._table + '" ' + \
+                            query = 'SELECT id FROM "' + cls._table + '" ' + \
                                     'WHERE ' + clause
                             if domain[i][1] == 'child_of':
                                 domain[i] = ('id', 'inselect', (query, []))
@@ -1673,19 +1662,19 @@
                                     ids2, table, domain[i][0]), table)
                 else:
                     if isinstance(domain[i][2], basestring):
-                        field_obj = pool.get(field.model_name)
+                        Field = pool.get(field.model_name)
                         m2o_search = [('rec_name', domain[i][1], domain[i][2])]
-                        if ('active' in field_obj._columns
-                                or 'active' in field_obj._inherit_fields):
+                        if ('active' in Field._fields
+                                or 'active' in Field._inherit_fields):
                             m2o_search += [('active', 'in', (True, False))]
-                        res_ids = field_obj.search(m2o_search, order=[])
+                        res_ids = map(int, Field.search(m2o_search, order=[]))
                         domain[i] = (domain[i][0], 'in', res_ids, table)
                     else:
                         domain[i] += (table,)
                 i += 1
             else:
                 if getattr(field, 'translate', False):
-                    if self._name == 'ir.model':
+                    if cls.__name__ == 'ir.model':
                         table_join = 'LEFT JOIN "ir_translation" ' \
                                 'ON (ir_translation.name = ' \
                                         'ir_model.model||\',%s\' ' \
@@ -1694,7 +1683,7 @@
                                     'AND ir_translation.type = \'model\' ' \
                                     'AND ir_translation.fuzzy = %%s)' % \
                                 domain[i][0]
-                    elif self._name == 'ir.model.field':
+                    elif cls.__name__ == 'ir.model.field':
                         if domain[i][0] == 'field_description':
                             ttype = 'field'
                         else:
@@ -1716,7 +1705,7 @@
                                     'AND ir_translation.lang = %%s ' \
                                     'AND ir_translation.type = \'model\' ' \
                                     'AND ir_translation.fuzzy = %%s)' % \
-                                (table._table, table._name, domain[i][0])
+                                (table._table, table.__name__, domain[i][0])
                     table_join_args = [Transaction().language, False]
 
                     table_query = ''
@@ -1725,10 +1714,10 @@
                         table_query, table_args = table.table_query()
                         table_query = '(' + table_query + ') AS '
 
-                    translation_obj = pool.get('ir.translation')
+                    Translation = pool.get('ir.translation')
 
                     qu1, qu2, tables, table_args = \
-                            translation_obj.search_domain([
+                            Translation.search_domain([
                                 ('value', domain[i][1], domain[i][2]),
                                 ])
                     qu1 = qu1.replace('"ir_translation"."value"',
@@ -1747,8 +1736,8 @@
 
         qu1, qu2 = [], []
         for arg in domain:
-            table = get_model_for_field(self, arg[0])
-            if table == self and len(arg) > 3:
+            table = get_model_for_field(cls, arg[0])
+            if table == cls and len(arg) > 3:
                 table = arg[3]
             column = column_get(table, arg[0])
             if arg[1] in ('inselect', 'notinselect'):
@@ -1887,7 +1876,8 @@
 
         return qu1, qu2
 
-    def _order_calc(self, field, otype):
+    @classmethod
+    def _order_calc(cls, field, otype):
         pool = Pool()
         order_by = []
         tables = []
@@ -1896,25 +1886,25 @@
         table_name = None
         link_field = None
 
-        if field in self._columns:
-            table_name = self._table
+        if field in cls._fields:
+            table_name = cls._table
 
-            if not hasattr(self._columns[field], 'set'):
+            if not hasattr(cls._fields[field], 'set'):
                 field_name = field
 
-            if self._columns[field].order_field:
-                field_name = self._columns[field].order_field
+            if cls._fields[field].order_field:
+                field_name = cls._fields[field].order_field
 
-            if isinstance(self._columns[field], fields.Many2One):
-                obj = pool.get(self._columns[field].model_name)
+            if isinstance(cls._fields[field], fields.Many2One):
+                obj = pool.get(cls._fields[field].model_name)
                 table_name = obj._table
                 link_field = field
                 field_name = None
 
-                if obj._rec_name in obj._columns:
+                if obj._rec_name in obj._fields:
                     field_name = obj._rec_name
 
-                if obj._order_name in obj._columns:
+                if obj._order_name in obj._fields:
                     field_name = obj._order_name
 
                 if field_name:
@@ -1923,7 +1913,7 @@
                     table_join = 'LEFT JOIN "' + table_name + '" AS ' \
                             '"' + table_name + '.' + link_field + '" ON ' \
                             '"%s.%s".id = "%s"."%s"' % (table_name, link_field,
-                                    self._table, link_field)
+                                    cls._table, link_field)
                     for i in range(len(order_by)):
                         if table_name in order_by[i]:
                             order_by[i] = order_by[i].replace(table_name,
@@ -1951,14 +1941,14 @@
 
                 if obj2 and field_name:
                     table_name2 = obj2._table
-                    link_field2 = obj._inherits[obj2._name]
+                    link_field2 = obj._inherits[obj2.__name__]
                     order_by, tables, tables_args = \
                             obj2._order_calc(field_name, otype)
 
                     table_join = 'LEFT JOIN "' + table_name + '" AS ' \
                             '"' + table_name + '.' + link_field + '" ON ' \
                             '"%s.%s".id = "%s"."%s"' % \
-                            (table_name, link_field, self._table, link_field)
+                            (table_name, link_field, cls._table, link_field)
                     for i in range(len(order_by)):
                         if table_name in order_by[i]:
                             order_by[i] = order_by[i].replace(table_name,
@@ -1993,11 +1983,11 @@
                         tables_args[table_join2] = []
                     return order_by, tables, tables_args
 
-            if field_name in self._columns \
-                    and getattr(self._columns[field_name], 'translate', False):
+            if field_name in cls._fields \
+                    and getattr(cls._fields[field_name], 'translate', False):
                 translation_table = 'ir_translation_%s_%s' % \
                         (table_name, field_name)
-                if self._name == 'ir.model':
+                if cls.__name__ == 'ir.model':
                     table_join = 'LEFT JOIN "ir_translation" ' \
                             'AS "%s" ON ' \
                             '("%s".name = "ir_model".model||\',%s\' ' \
@@ -2008,7 +1998,7 @@
                             (translation_table, translation_table, field_name,
                                     translation_table, translation_table,
                                     translation_table, translation_table)
-                elif self._name == 'ir.model.field':
+                elif cls.__name__ == 'ir.model.field':
                     if field_name == 'field_description':
                         ttype = 'field'
                     else:
@@ -2037,9 +2027,9 @@
                                 'AND "%s".type = \'model\' ' \
                                 'AND "%s".fuzzy = %%s)' % \
                             (translation_table, translation_table, table_name,
-                                    translation_table, self._name, field_name,
-                                    translation_table, translation_table,
-                                    translation_table)
+                                translation_table, cls.__name__, field_name,
+                                translation_table, translation_table,
+                                translation_table)
                 if table_join not in tables:
                     tables.append(table_join)
                     tables_args[table_join] = [Transaction().language, False]
@@ -2048,14 +2038,14 @@
                         + '"' + table_name + '".' + field_name + ') ' + otype)
                 return order_by, tables, tables_args
 
-            if field_name in self._columns \
-                    and self._columns[field_name]._type == 'selection' \
-                    and self._columns[field_name].order_field is None:
-                selections = self.fields_get([field_name]
+            if field_name in cls._fields \
+                    and cls._fields[field_name]._type == 'selection' \
+                    and cls._fields[field_name].order_field is None:
+                selections = cls.fields_get([field_name]
                         )[field_name]['selection']
                 if not isinstance(selections, (tuple, list)):
-                    selections = getattr(self,
-                            self._columns[field_name].selection)()
+                    selections = getattr(cls,
+                            cls._fields[field_name].selection)()
                 order = 'CASE ' + table_name + '.' + field_name
                 for selection in selections:
                     order += ' WHEN \'%s\' THEN \'%s\'' % selection
@@ -2074,22 +2064,23 @@
                         % (table_name, field_name, otype))
                 return order_by, tables, tables_args
 
-        if field in self._inherit_fields.keys():
-            obj = pool.get(self._inherit_fields[field][0])
+        if field in cls._inherit_fields.keys():
+            obj = pool.get(cls._inherit_fields[field][0])
             table_name = obj._table
-            link_field = self._inherits[obj._name]
+            link_field = cls._inherits[obj.__name__]
             order_by, tables, tables_args = obj._order_calc(field, otype)
             table_join = 'LEFT JOIN "' + table_name + '" ON ' \
                     '"%s".id = "%s"."%s"' % \
-                    (table_name, self._table, link_field)
+                    (table_name, cls._table, link_field)
             if table_join not in tables:
                 tables.insert(0, table_join)
             return order_by, tables, tables_args
 
         raise Exception('Error', 'Wrong field name (%s) for %s in order!' %
-            (field, self._name))
+            (field, cls.__name__))
 
-    def _rebuild_tree(self, parent, parent_id, left):
+    @classmethod
+    def _rebuild_tree(cls, parent, parent_id, left):
         '''
         Rebuild left, right value for the tree.
         '''
@@ -2097,23 +2088,24 @@
         right = left + 1
 
         with Transaction().set_user(0):
-            child_ids = self.search([
-                (parent, '=', parent_id),
-                ])
+            childs = cls.search([
+                    (parent, '=', parent_id),
+                    ])
 
-        for child_id in child_ids:
-            right = self._rebuild_tree(parent, child_id, right)
+        for child in childs:
+            right = cls._rebuild_tree(parent, child.id, right)
 
-        field = self._columns[parent]
+        field = cls._fields[parent]
 
         if parent_id:
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET "' + field.left + '" = %s, ' \
                         '"' + field.right + '" = %s ' \
                     'WHERE id = %s', (left, right, parent_id))
         return right + 1
 
-    def _update_tree(self, object_id, field_name, left, right):
+    @classmethod
+    def _update_tree(cls, object_id, field_name, left, right):
         '''
         Update left, right values for the tree.
         Remarks:
@@ -2123,14 +2115,14 @@
         '''
         cursor = Transaction().cursor
         cursor.execute('SELECT "' + left + '", "' + right + '" ' \
-                'FROM "' + self._table + '" ' \
+                'FROM "' + cls._table + '" ' \
                 'WHERE id = %s', (object_id,))
         fetchone = cursor.fetchone()
         if not fetchone:
             return
         old_left, old_right = fetchone
         if old_left == old_right:
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET "' + right + '" = "' + right + '" + 1 ' \
                     'WHERE id = %s', (object_id,))
             old_right += 1
@@ -2138,42 +2130,42 @@
         parent_right = 1
 
         cursor.execute('SELECT "' + field_name + '" ' \
-                'FROM "' + self._table + '" ' \
+                'FROM "' + cls._table + '" ' \
                 'WHERE id = %s', (object_id,))
         parent_id = cursor.fetchone()[0] or False
 
         if parent_id:
             cursor.execute('SELECT "' + right + '" ' \
-                    'FROM "' + self._table + '" ' \
+                    'FROM "' + cls._table + '" ' \
                     'WHERE id = %s', (parent_id,))
             parent_right = cursor.fetchone()[0]
         else:
             cursor.execute('SELECT MAX("' + right + '") ' \
-                    'FROM "' + self._table + '" ' \
+                    'FROM "' + cls._table + '" ' \
                     'WHERE "' + field_name + '" IS NULL')
             fetchone = cursor.fetchone()
             if fetchone:
                 parent_right = fetchone[0] + 1
 
-        cursor.execute('SELECT id FROM "' + self._table + '" ' \
+        cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                 'WHERE "' + left + '" >= %s AND "' + right + '" <= %s',
                 (old_left, old_right))
         child_ids = [x[0] for x in cursor.fetchall()]
 
         if len(child_ids) > cursor.IN_MAX:
             with Transaction().set_user(0):
-                return self._rebuild_tree(field_name, False, 0)
+                return cls._rebuild_tree(field_name, False, 0)
 
         red_child_sql, red_child_ids = reduce_ids('id', child_ids)
         # ids for left update
-        cursor.execute('SELECT id FROM "' + self._table + '" ' \
+        cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                 'WHERE "' + left + '" >= %s ' \
                     'AND NOT ' + red_child_sql,
                     [parent_right] + red_child_ids)
         left_ids = [x[0] for x in cursor.fetchall()]
 
         # ids for right update
-        cursor.execute('SELECT id FROM "' + self._table + '" ' \
+        cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                 'WHERE "' + right + '" >= %s ' \
                     'AND NOT ' + red_child_sql,
                     [parent_right] + red_child_ids)
@@ -2183,7 +2175,7 @@
             for i in range(0, len(left_ids), cursor.IN_MAX):
                 sub_ids = left_ids[i:i + cursor.IN_MAX]
                 red_sub_sql, red_sub_ids = reduce_ids('id', sub_ids)
-                cursor.execute('UPDATE "' + self._table + '" ' \
+                cursor.execute('UPDATE "' + cls._table + '" ' \
                         'SET "' + left + '" = "' + left + '" + ' \
                             + str(old_right - old_left + 1) + ' ' \
                         'WHERE ' + red_sub_sql, red_sub_ids)
@@ -2191,12 +2183,12 @@
             for i in range(0, len(right_ids), cursor.IN_MAX):
                 sub_ids = right_ids[i:i + cursor.IN_MAX]
                 red_sub_sql, red_sub_ids = reduce_ids('id', sub_ids)
-                cursor.execute('UPDATE "' + self._table + '" ' \
+                cursor.execute('UPDATE "' + cls._table + '" ' \
                         'SET "' + right + '" = "' + right + '" + ' \
                             + str(old_right - old_left + 1) + ' ' \
                         'WHERE ' + red_sub_sql, red_sub_ids)
 
-        cursor.execute('UPDATE "' + self._table + '" ' \
+        cursor.execute('UPDATE "' + cls._table + '" ' \
                 'SET "' + left + '" = "' + left + '" + ' \
                         + str(parent_right - old_left) + ', ' \
                     '"' + right + '" = "' + right + '" + ' \
@@ -2206,21 +2198,21 @@
         # Use root user to by-pass rules
         with contextlib.nested(Transaction().set_user(0),
                 Transaction().set_context(active_test=False)):
-            brother_ids = self.search([
-                (field_name, '=', parent_id),
-                ])
+            brother_ids = map(int, cls.search([
+                        (field_name, '=', parent_id),
+                        ]))
         if brother_ids[-1] != object_id:
             next_id = brother_ids[brother_ids.index(object_id) + 1]
             cursor.execute('SELECT "' + left + '" ' \
-                    'FROM "' + self._table + '" ' \
+                    'FROM "' + cls._table + '" ' \
                     'WHERE id = %s', (next_id,))
             next_left = cursor.fetchone()[0]
             cursor.execute('SELECT "' + left + '" '\
-                    'FROM "' + self._table + '" ' \
+                    'FROM "' + cls._table + '" ' \
                     'WHERE id = %s', (object_id,))
             current_left = cursor.fetchone()[0]
 
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET "' + left + '" = "' + left + '" + ' \
                             + str(old_right - old_left + 1) + ', ' \
                         '"' + right + '" = "' + right + '" + ' \
@@ -2228,20 +2220,22 @@
                     'WHERE "' + left + '" >= %s AND "' + right + '" <= %s',
                     (next_left, current_left))
 
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET "' + left + '" = "' + left + '" - ' \
                             + str(current_left - next_left) + ', ' \
                         '"' + right + '" = "' + right + '" - ' \
                             + str(current_left - next_left) + ' ' \
                     'WHERE ' + red_child_sql, red_child_ids)
 
-    def _validate(self, ids):
-        super(ModelSQL, self)._validate(ids)
+    @classmethod
+    def _validate(cls, records):
+        super(ModelSQL, cls)._validate(records)
         cursor = Transaction().cursor
         if cursor.has_constraint():
             return
         # Works only for a single transaction
-        for _, sql, error in self._sql_constraints:
+        ids = map(int, records)
+        for _, sql, error in cls._sql_constraints:
             match = _RE_UNIQUE.match(sql)
             if match:
                 sql = match.group(1)
@@ -2254,18 +2248,18 @@
                     red_sql, red_ids = reduce_ids('id', sub_ids)
 
                     cursor.execute('SELECT id,' + sql + ' ' \
-                            'FROM "' + self._table + '" ' \
+                            'FROM "' + cls._table + '" ' \
                             'WHERE ' + red_sql, red_ids)
 
                     fetchall = cursor.fetchall()
                     cursor.execute('SELECT id ' \
-                            'FROM "' + self._table + '" ' \
+                            'FROM "' + cls._table + '" ' \
                             'WHERE ' + \
                                 ' OR '.join((sql_clause,) * len(fetchall)),
                             reduce(lambda x, y: x + list(y), fetchall, []))
 
                     if cursor.fetchone():
-                        self.raise_user_error(error)
+                        cls.raise_user_error(error)
                 continue
             match = _RE_CHECK.match(sql)
             if match:
@@ -2274,9 +2268,9 @@
                     sub_ids = ids[i:i + cursor.IN_MAX]
                     red_sql, red_ids = reduce_ids('id', sub_ids)
                     cursor.execute('SELECT id ' \
-                            'FROM "' + self._table + '" ' \
+                            'FROM "' + cls._table + '" ' \
                             'WHERE NOT (' + sql + ') ' \
                                 'AND ' + red_sql, red_ids)
                     if cursor.fetchone():
-                        self.raise_user_error(error)
+                        cls.raise_user_error(error)
                     continue
diff -r fb51f5b4aa4c trytond/model/modelstorage.py
--- a/trytond/model/modelstorage.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/modelstorage.py	Mon Sep 24 20:36:36 2012 +0200
@@ -3,31 +3,34 @@
 
 import datetime
 import time
-from decimal import Decimal
 import logging
-from itertools import chain
+import contextlib
+import traceback
+import sys
+import csv
 try:
     import cStringIO as StringIO
 except ImportError:
     import StringIO
-import csv
+
+from decimal import Decimal
+from itertools import islice, ifilter, ifilterfalse, chain
 from functools import reduce
-import traceback
-import sys
+
 from trytond.model import Model
 from trytond.model import fields
-from trytond.model.browse import BrowseRecordList, BrowseRecord, \
-        BrowseRecordNull
-from trytond.model.browse import EvalEnvironment
-from trytond.tools import safe_eval, reduce_domain, datetime_strftime
+from trytond.tools import safe_eval, reduce_domain
 from trytond.pyson import PYSONEncoder, PYSONDecoder, PYSON
-from trytond.const import OPERATORS, RECORD_CACHE_SIZE
+from trytond.const import OPERATORS, RECORD_CACHE_SIZE, BROWSE_FIELD_TRESHOLD
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 from trytond.cache import LRUDict
 from trytond.config import CONFIG
+from trytond.rpc import RPC
 from .modelview import ModelView
 
+__all__ = ['ModelStorage']
+
 
 class ModelStorage(Model):
     """
@@ -41,237 +44,211 @@
     rec_name = fields.Function(fields.Char('Name'), 'get_rec_name',
             searcher='search_rec_name')
 
-    def __init__(self):
-        super(ModelStorage, self).__init__()
-        if isinstance(self, ModelView):
-            self._rpc.update({
-                'create': True,
-                'read': False,
-                'write': True,
-                'delete': True,
-                'copy': True,
-                'search': False,
-                'search_count': False,
-                'search_read': False,
-                'export_data': False,
-                'import_data': True,
-            })
-        self._constraints = []
+    @classmethod
+    def __setup__(cls):
+        super(ModelStorage, cls).__setup__()
+        if issubclass(cls, ModelView):
+            cls.__rpc__.update({
+                    'create': RPC(readonly=False, result=int),
+                    'read': RPC(),
+                    'write': RPC(readonly=False, instantiate=0),
+                    'delete': RPC(readonly=False, instantiate=0),
+                    'copy': RPC(readonly=False, instantiate=0,
+                        result=lambda r: map(int, r)),
+                    'search': RPC(result=lambda r: map(int, r)),
+                    'search_count': RPC(),
+                    'search_read': RPC(),
+                    'export_data': RPC(instantiate=0),
+                    'import_data': RPC(readonly=False),
+                    })
+        cls._constraints = []
 
-    def default_create_uid(self):
+    @staticmethod
+    def default_create_uid():
         "Default value for uid field."
         return int(Transaction().user)
 
-    def default_create_date(self):
+    @staticmethod
+    def default_create_date():
         "Default value for create_date field."
         return datetime.datetime.today()
 
-    def __fields_by_model(self, values):
+    @classmethod
+    def __fields_by_model(cls, values):
         "Return a dictionary with list of fields from values grouped by model"
         fields_by_model = {}
         for field in values:
-            if field in self._columns:
-                model = self._name
+            if field in cls._fields:
+                model = cls.__name__
             else:
-                model = self._inherit_fields[field][0]
+                model = cls._inherit_fields[field][0]
             fields_by_model.setdefault(model, []).append(field)
         return fields_by_model
 
-    def create(self, values):
+    @classmethod
+    def create(cls, values):
         '''
-        Create records.
-
-        :param values: a dictionary with fields names as key
-                and created values as value
-        :return: the id of the created record
+        Create record and return an instance.
         '''
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
-        model_field_access_obj = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
+        ModelFieldAccess = pool.get('ir.model.field.access')
 
-        model_access_obj.check(self._name, 'create')
-        for model, fields in self.__fields_by_model(values).iteritems():
-            model_field_access_obj.check(model, fields, 'write')
-        return False
+        ModelAccess.check(cls.__name__, 'create')
+        for model, fields in cls.__fields_by_model(values).iteritems():
+            ModelFieldAccess.check(model, fields, 'write')
 
-    def trigger_create(self, id):
+        # Increase transaction counter
+        Transaction().counter += 1
+
+    @classmethod
+    def trigger_create(cls, record):
         '''
         Trigger create actions
+        '''
+        Trigger = Pool().get('ir.trigger')
+        triggers = Trigger.get_triggers(cls.__name__, 'create')
+        if not triggers:
+            return
+        for trigger in triggers:
+            if Trigger.eval(trigger, record):
+                Trigger.trigger_action([record], trigger)
 
-        :param id: the created id
+    @classmethod
+    def read(cls, ids, fields_names=None):
         '''
-        trigger_obj = Pool().get('ir.trigger')
-        trigger_ids = trigger_obj.get_triggers(self._name, 'create')
-        if not trigger_ids:
-            return
-        record = self.browse(id)
-        triggers = trigger_obj.browse(trigger_ids)
-        for trigger in triggers:
-            if trigger_obj.eval(trigger, record):
-                trigger_obj.trigger_action([id], trigger.id)
-
-    def read(self, ids, fields_names=None):
-        '''
-        Read records.
-
-        :param ids: a list of ids or an id
-        :param fields_names: fields names to read if None read all fields
-        :return: a list of dictionnary or a dictionnary if ids is an id
-            the dictionnaries will have fields names as key
-            and fields value as value. The list will not be in the same order.
+        Read fields_names of record ids.
+        If fields_names is None, it read all fields.
+        The order is not guaranteed.
         '''
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
-        model_field_access_obj = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
+        ModelFieldAccess = pool.get('ir.model.field.access')
 
-        model_access_obj.check(self._name, 'read')
-        model_field_access_obj.check(self._name,
-                fields_names or self._columns.keys(), 'read')
+        ModelAccess.check(cls.__name__, 'read')
+        ModelFieldAccess.check(cls.__name__,
+                fields_names or cls._fields.keys(), 'read')
         if isinstance(ids, (int, long)):
             return {}
         return []
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, records, values):
         '''
         Write values on records.
-
-        :param ids: a list of ids or an id
-        :param values: a dictionary with fields names as key
-                and written values as value
-        :return: True if succeed
         '''
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
-        model_field_access_obj = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
+        ModelFieldAccess = pool.get('ir.model.field.access')
 
-        model_access_obj.check(self._name, 'write')
-        for model, fields in self.__fields_by_model(values).iteritems():
-            model_field_access_obj.check(model, fields, 'write')
-        if not self.check_xml_record(ids, values):
-            self.raise_user_error('write_xml_record',
+        ModelAccess.check(cls.__name__, 'write')
+        for model, fields in cls.__fields_by_model(values).iteritems():
+            ModelFieldAccess.check(model, fields, 'write')
+        if not cls.check_xml_record(records, values):
+            cls.raise_user_error('write_xml_record',
                     error_description='xml_record_desc')
 
+        # Increase transaction counter
+        Transaction().counter += 1
+
+        # Clean local cache
+        for record in records:
+            local_cache = record._local_cache.get(record.id)
+            if local_cache:
+                local_cache.clear()
+
+        # Clean cursor cache
+        for cache in Transaction().cursor.cache.itervalues():
+            if cls.__name__ in cache:
+                for record in records:
+                    if record.id in cache[cls.__name__]:
+                        cache[cls.__name__][record.id].clear()
+
+    @classmethod
+    def trigger_write_get_eligibles(cls, records):
+        '''
+        Return eligible records for write actions by triggers
+        '''
+        Trigger = Pool().get('ir.trigger')
+        triggers = Trigger.get_triggers(cls.__name__, 'write')
+        if not triggers:
+            return {}
+        eligibles = {}
+        for trigger in triggers:
+            eligibles[trigger] = []
+            for record in records:
+                if not Trigger.eval(trigger, record):
+                    eligibles[trigger].append(record)
+        return eligibles
+
+    @classmethod
+    def trigger_write(cls, eligibles):
+        '''
+        Trigger write actions.
+        eligibles is a dictionary of the lists of eligible records by triggers
+        '''
+        Trigger = Pool().get('ir.trigger')
+        for trigger, records in eligibles.iteritems():
+            triggered = []
+            for record in records:
+                if Trigger.eval(trigger, record):
+                    triggered.append(record)
+            if triggered:
+                Trigger.trigger_action(triggered, trigger)
+
+    @classmethod
+    def delete(cls, records):
+        '''
+        Delete records.
+        '''
+        ModelAccess = Pool().get('ir.model.access')
+
+        ModelAccess.check(cls.__name__, 'delete')
+        if not cls.check_xml_record(records, None):
+            cls.raise_user_error('delete_xml_record',
+                    error_description='xml_record_desc')
+
+        # Increase transaction counter
+        Transaction().counter += 1
+
         # Clean cursor cache
         for cache in Transaction().cursor.cache.values():
             for cache in (cache, cache.get('_language_cache', {}).values()):
-                if self._name in cache:
-                    if isinstance(ids, (int, long)):
-                        ids = [ids]
-                    for i in ids:
-                        if i in cache[self._name]:
-                            cache[self._name][i] = {}
-        return False
+                if cls.__name__ in cache:
+                    for record in records:
+                        if record.id in cache[cls.__name__]:
+                            del cache[cls.__name__][record.id]
 
-    def trigger_write_get_eligibles(self, ids):
-        '''
-        Return eligible ids for write actions by triggers
-
-        :param ids: a list of ids
-        :return: a dictionary of the lists of eligible ids by triggers
-        '''
-        trigger_obj = Pool().get('ir.trigger')
-        trigger_ids = trigger_obj.get_triggers(self._name, 'write')
-        if not trigger_ids:
-            return {}
-        records = self.browse(ids)
-        triggers = trigger_obj.browse(trigger_ids)
-        eligibles = {}
-        for trigger in triggers:
-            eligibles[trigger.id] = []
-            for record in records:
-                if not trigger_obj.eval(trigger, record):
-                    eligibles[trigger.id].append(record.id)
-        return eligibles
-
-    def trigger_write(self, eligibles):
-        '''
-        Trigger write actions
-
-        :param eligibles: a dictionary of the lists of eligible ids by triggers
-        '''
-        trigger_obj = Pool().get('ir.trigger')
-        trigger_ids = eligibles.keys()
-        if not trigger_ids:
-            return
-        records = self.browse(list(chain(*eligibles.values())))
-        id2record = dict((x.id, x) for x in records)
-        triggers = trigger_obj.browse(trigger_ids)
-        for trigger in triggers:
-            triggered_ids = []
-            for record_id in eligibles[trigger.id]:
-                record = id2record[record_id]
-                if trigger_obj.eval(trigger, record):
-                    triggered_ids.append(record.id)
-            if triggered_ids:
-                trigger_obj.trigger_action(triggered_ids, trigger.id)
-
-    def delete(self, ids):
-        '''
-        Delete records.
-
-        :param ids: a list of ids or an id
-        :return: True if succeed
-        '''
-        model_access_obj = Pool().get('ir.model.access')
-
-        model_access_obj.check(self._name, 'delete')
-        if not self.check_xml_record(ids, None):
-            self.raise_user_error('delete_xml_record',
-                    error_description='xml_record_desc')
-
-        # Clean cursor cache
-        for cache in Transaction().cursor.cache.values():
-            for cache in (cache, cache.get('_language_cache', {}).values()):
-                if self._name in cache:
-                    if isinstance(ids, (int, long)):
-                        ids = [ids]
-                    for i in ids:
-                        if i in cache[self._name]:
-                            del cache[self._name][i]
-        return False
-
-    def trigger_delete(self, ids):
+    @classmethod
+    def trigger_delete(cls, records):
         '''
         Trigger delete actions
+        '''
+        Trigger = Pool().get('ir.trigger')
+        triggers = Trigger.get_triggers(cls.__name__, 'delete')
+        if not triggers:
+            return
+        for trigger in triggers:
+            triggered = []
+            for record in records:
+                if Trigger.eval(trigger, record):
+                    triggered.append(record)
+            if triggered:
+                Trigger.trigger_action(triggered, trigger)
 
-        :param ids: the deleted ids
+    @classmethod
+    def copy(cls, records, default=None):
         '''
-        trigger_obj = Pool().get('ir.trigger')
-        trigger_ids = trigger_obj.get_triggers(self._name, 'delete')
-        if not trigger_ids:
-            return
-        records = self.browse(ids)
-        triggers = trigger_obj.browse(trigger_ids)
-        for trigger in triggers:
-            triggered_ids = []
-            for record in records:
-                if trigger_obj.eval(trigger, record):
-                    triggered_ids.append(record.id)
-            if triggered_ids:
-                trigger_obj.trigger_action(triggered_ids, trigger.id)
-
-    def copy(self, ids, default=None):
-        '''
-        Duplicate the record(s) in ids.
-
-        :param ids: a list of ids or an id
-        :param default: a dictionary with field name as keys and
-            new value for the field as value
-        :return: a list of new ids or the new id
+        Duplicate the records and return a list of new records.
         '''
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
+        Lang = pool.get('ir.lang')
         if default is None:
             default = {}
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
         if 'state' not in default:
-            if 'state' in self._defaults:
-                default['state'] = self._defaults['state']()
+            if 'state' in cls._defaults:
+                default['state'] = cls._defaults['state']()
 
         def convert_data(field_defs, data):
             data = data.copy()
@@ -309,145 +286,94 @@
             return data, data_o2m
 
         new_ids = {}
-        fields_names = self._columns.keys()
-        datas = self.read(ids, fields_names=fields_names)
-        field_defs = self.fields_get(fields_names=fields_names)
+        fields_names = cls._fields.keys()
+        ids = map(int, records)
+        datas = cls.read(ids, fields_names=fields_names)
+        field_defs = cls.fields_get(fields_names=fields_names)
         for data in datas:
             data_id = data['id']
             data, data_o2m = convert_data(field_defs, data)
-            new_ids[data_id] = self.create(data)
+            new_record = cls.create(data)
+            if new_record:
+                new_ids[data_id] = new_record.id
+            else:
+                continue
             for field_name in data_o2m:
-                relation_model = pool.get(
+                Relation = pool.get(
                         field_defs[field_name]['relation'])
                 relation_field = field_defs[field_name]['relation_field']
                 if relation_field:
-                    relation_model.copy(data_o2m[field_name],
-                            default={relation_field: new_ids[data_id]})
+                    Relation.copy(Relation.browse(data_o2m[field_name]),
+                        default={relation_field: new_ids[data_id]})
 
         fields_translate = {}
         for field_name, field in field_defs.iteritems():
-            if field_name in self._columns and \
-                    getattr(self._columns[field_name], 'translate', False):
+            if field_name in cls._fields and \
+                    getattr(cls._fields[field_name], 'translate', False):
                 fields_translate[field_name] = field
 
         if fields_translate:
-            lang_ids = lang_obj.search([
+            lang_ids = Lang.search([
                 ('translatable', '=', True),
                 ])
             if lang_ids:
-                lang_ids += lang_obj.search([
+                lang_ids += Lang.search([
                     ('code', '=', CONFIG['language']),
                     ])
-                langs = lang_obj.browse(lang_ids)
+                langs = Lang.browse(lang_ids)
                 for lang in langs:
                     with Transaction().set_context(language=lang.code):
-                        datas = self.read(ids,
+                        datas = cls.read(ids,
                                 fields_names=fields_translate.keys() + ['id'])
                         for data in datas:
                             data_id = data['id']
                             data, _ = convert_data(fields_translate, data)
-                            self.write(new_ids[data_id], data)
-        if int_id:
-            return new_ids.values()[0]
-        return new_ids.values()
+                            cls.write([cls(new_ids[data_id])], data)
+        return cls.browse(new_ids.values())
 
-    def search(self, domain, offset=0, limit=None, order=None, count=False):
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False):
         '''
-        Return a list of ids that match the domain.
-
-        :param domain: a list of tuples or lists
-            lists are constructed like this:
-            ``['operator', args, args, ...]``
-            operator can be 'AND' or 'OR', if it is missing the default
-            value will be 'AND'
-            tuples are constructed like this:
-            ``('field name', 'operator', value)``
-            field name: is a field name from the model or a relational field
-            by using '.' as separator.
-            operator must be in OPERATORS
-        :param offset: an integer to specify the offset of the result
-        :param limit: an integer to specify the number of result
-        :param order: a list of tuples that are constructed like this:
-            ``('field name', 'DESC|ASC')``
-            allowing to specify the order of result
-        :param count: a boolean to return only the length of the result
-        :return: a list of ids or an integer
+        Return a list of records that match the domain.
         '''
         if count:
             return 0
         return []
 
-    def search_count(self, domain):
+    @classmethod
+    def search_count(cls, domain):
         '''
-        Return the number of records that match the domain. (See search)
-
-        :param domain: a domain like in search
-        :return: an integer
+        Return the number of records that match the domain.
         '''
-        res = self.search(domain, count=True)
+        res = cls.search(domain, count=True)
         if isinstance(res, list):
             return len(res)
         return res
 
-    def search_read(self, domain, offset=0, limit=None, order=None,
+    @classmethod
+    def search_read(cls, domain, offset=0, limit=None, order=None,
             fields_names=None):
         '''
         Call search and read functions at once.
         Useful for the client to reduce the number of calls.
-
-        :param domain: a domain like in search
-        :param offset: an integer to specify the offset for the result
-        :param limit: an integer to specify the number of records
-        :param order: a list of tuples that are constructed like this:
-            ('field name', 'DESC|ASC')
-            allowing to to specify the order of result
-        :param fields_names: fields names to read if None read all fields
-        :return: a list of dictionaries or a dictionary if limit is 1
-            the dictionaries will have field names as key
-            and field values as value
         '''
-        ids = self.search(domain, offset=offset, limit=limit, order=order)
+        records = cls.search(domain, offset=offset, limit=limit, order=order)
 
         if not fields_names:
-            fields_names = list(set(self._columns.keys() \
-                    + self._inherit_fields.keys()))
+            fields_names = list(set(cls._fields.keys() \
+                    + cls._inherit_fields.keys()))
         if 'id' not in fields_names:
             fields_names.append('id')
+        return cls.read(map(int, records), fields_names)
 
-        res = []
-        for model in self.browse(ids):
-            record = {}
-            for fields_name in set(fields_names):
-                fields_split = fields_name.split('.')
-                while fields_split:
-                    field_name = fields_split.pop(0)
-                    if fields_name not in record:
-                        record[fields_name] = model[field_name]
-                    else:
-                        if isinstance(record[fields_name], BrowseRecordNull):
-                            continue
-                        record[fields_name] = record[fields_name][field_name]
-                if isinstance(record[fields_name], BrowseRecordNull):
-                    record[fields_name] = False
-                elif isinstance(record[fields_name], BrowseRecord):
-                    record[fields_name] = record[fields_name].id
-                elif isinstance(record[fields_name], BrowseRecordList):
-                    record[fields_name] = [x.id for x in record[fields_name]]
-            res.append(record)
-
-        if limit == 1:
-            if not ids:
-                return []
-            return res[0]
-        return res
-
-    def _search_domain_active(self, domain, active_test=True):
+    @classmethod
+    def _search_domain_active(cls, domain, active_test=True):
         # reduce_domain return a new instance so we can safety modify domain
         domain = reduce_domain(domain)
         # if the object has a field named 'active', filter out all inactive
         # records unless they were explicitely asked for
-        if not (('active' in self._columns
-            or 'active' in self._inherit_fields.keys())
+        if not (('active' in cls._fields
+            or 'active' in cls._inherit_fields.keys())
             and (active_test
                 and Transaction().context.get('active_test', True))):
             return domain
@@ -476,55 +402,39 @@
             return domain
         return process(domain)
 
-    def get_rec_name(self, ids, name):
+    def get_rec_name(self, name):
         '''
-        Return a dictionary with id as key and rec_name as value.
+        Return the rec_name of the instance.
         It is used by the Function field rec_name.
-
-        :param ids: a list of ids
-        :param name: the name of the Function field
-        :return: a dictionary
         '''
-        if not ids:
-            return {}
-        res = {}
         rec_name = self._rec_name
-        if (rec_name not in self._columns
+        if (rec_name not in self._fields
                 and rec_name not in self._inherit_fields):
             rec_name = 'id'
-        for record in self.browse(ids):
-            res[record.id] = unicode(record[rec_name])
-        return res
+        return unicode(getattr(self, rec_name))
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         '''
         Return a list of arguments for search on rec_name.
-
-        :param name: the name of the Function field
-        :param clause: a domain clause
-        :return: a list of domain clause
         '''
-        rec_name = self._rec_name
-        if (rec_name not in self._columns
-                and rec_name not in self._inherit_fields):
+        rec_name = cls._rec_name
+        if (rec_name not in cls._fields
+                and rec_name not in cls._inherit_fields):
             return []
         return [(rec_name,) + clause[1:]]
 
-    def browse(self, ids):
+    @classmethod
+    def browse(cls, ids):
         '''
-        Return a BrowseRecordList for the ids
-            or BrowseRecord if ids is a integer.
+        Return a list of instance for the ids
+        '''
+        ids = map(int, ids)
+        local_cache = LRUDict(RECORD_CACHE_SIZE)
+        return [cls(int(x), _ids=ids, _local_cache=local_cache) for x in ids]
 
-        :param ids: a list of ids or an id
-        :return: a BrowseRecordList or a BrowseRecord
-        '''
-        local_cache = LRUDict(RECORD_CACHE_SIZE)
-        if isinstance(ids, (int, long)):
-            return BrowseRecord(ids, self, [ids], local_cache)
-        return BrowseRecordList((BrowseRecord(x, self, ids, local_cache)
-            for x in ids))
-
-    def __export_row(self, record, fields_names):
+    @staticmethod
+    def __export_row(record, fields_names):
         pool = Pool()
         lines = []
         data = ['' for x in range(len(fields_names))]
@@ -536,21 +446,21 @@
             value = record
             i = 0
             while i < len(fields_tree):
-                if not isinstance(value, BrowseRecord):
+                if not isinstance(value, ModelStorage):
                     break
                 field_name = fields_tree[i]
-                model_obj = pool.get(value._model_name)
-                if field_name in model_obj._columns:
-                    field = model_obj._columns[field_name]
-                elif field_name in model_obj._inherit_fields:
-                    field = model_obj._inherit_fields[field_name][2]
+                eModel = pool.get(value.__name__)
+                if field_name in eModel._fields:
+                    field = eModel._fields[field_name]
+                elif field_name in eModel._inherit_fields:
+                    field = eModel._inherit_fields[field_name][2]
                 else:
                     raise Exception('Field %s not available on object "%s"'
-                        % (field_name, model_obj._name))
+                        % (field_name, eModel.__name__))
                 if field.states and 'invisible' in field.states:
                     pyson_invisible = PYSONEncoder().encode(
                             field.states['invisible'])
-                    env = EvalEnvironment(value, model_obj)
+                    env = EvalEnvironment(value, eModel)
                     env.update(Transaction().context)
                     env['current_date'] = datetime.datetime.today()
                     env['time'] = time
@@ -560,8 +470,8 @@
                     if invisible:
                         value = ''
                         break
-                value = value[field_name]
-                if isinstance(value, (BrowseRecordList, list)):
+                value = getattr(value, field_name)
+                if isinstance(value, (list, tuple)):
                     first = True
                     child_fields_names = [(x[:i + 1] == fields_tree[:i + 1] and
                         x[i + 1:]) or [] for x in fields_names]
@@ -569,7 +479,7 @@
                         break
                     done.append(child_fields_names)
                     for child_record in value:
-                        child_lines = self.__export_row(child_record,
+                        child_lines = ModelStorage.__export_row(child_record,
                                 child_fields_names)
                         if first:
                             for child_fpos in xrange(len(fields_names)):
@@ -585,33 +495,30 @@
             if i == len(fields_tree):
                 if value is None:
                     value = ''
+                elif isinstance(value, Model):
+                    value = str(value)
                 data[fpos] = value
         return [data] + lines
 
-    def export_data(self, ids, fields_names):
+    @classmethod
+    def export_data(cls, records, fields_names):
         '''
-        Return list of list of values for each id in ids.
+        Return list of list of values for each record.
         The list of values follows fields_names.
         Relational fields are defined with '/' at any depth.
-
-        :param ids: a list of ids
-        :param fields_names: a list of field names
-        :return: a list of list of values for each id in ids
         '''
         fields_names = [x.split('/') for x in fields_names]
-        datas = []
-        for record in self.browse(ids):
-            datas += self.__export_row(record, fields_names)
-        return datas
+        data = []
+        for record in records:
+            data += cls.__export_row(record, fields_names)
+        return data
 
-    def import_data(self, fields_names, datas):
+    @classmethod
+    def import_data(cls, fields_names, data):
         '''
-        Create records for all values in datas.
+        Create records for all values in data.
         The field names of values must be defined in fields_names.
-
-        :param fields_names: a list of fields names
-        :param datas: the data to import
-        :return: a tuple with
+        It returns a tuple with
             - the number of records imported
             - the last values if failed
             - the exception if failed
@@ -619,17 +526,17 @@
         '''
         pool = Pool()
 
-        def process_lines(self, datas, prefix, fields_def, position=0):
+        def process_lines(data, prefix, fields_def, position=0):
 
             def warn(msgname, *args):
-                msg = self.raise_user_error(msgname, args,
+                msg = cls.raise_user_error(msgname, args,
                         raise_exception=False)
                 warnings.warn(msg)
 
             def get_selection(selection, value):
                 res = None
                 if not isinstance(selection, (tuple, list)):
-                    selection = getattr(self, selection)()
+                    selection = getattr(cls, selection)()
                 for key, _ in selection:
                     if str(key) == value:
                         res = key
@@ -641,8 +548,8 @@
             def get_many2one(relation, value):
                 if not value:
                     return None
-                relation_obj = pool.get(relation)
-                res = relation_obj.search([
+                Relation = pool.get(relation)
+                res = Relation.search([
                     ('rec_name', '=', value),
                     ], limit=2)
                 if len(res) < 1:
@@ -659,10 +566,10 @@
                 if not value:
                     return None
                 res = []
-                relation_obj = pool.get(relation)
+                Relation = pool.get(relation)
                 for word in csv.reader(StringIO.StringIO(value), delimiter=',',
                         quoting=csv.QUOTE_NONE, escapechar='\\').next():
-                    res2 = relation_obj.search([
+                    res2 = Relation.search([
                         ('rec_name', '=', word),
                         ], limit=2)
                     if len(res2) < 1:
@@ -686,8 +593,8 @@
                 except Exception:
                     warn('reference_syntax_error', value, '/'.join(field))
                     return None
-                relation_obj = pool.get(relation)
-                res = relation_obj.search([
+                Relation = pool.get(relation)
+                res = Relation.search([
                     ('rec_name', '=', value),
                     ], limit=2)
                 if len(res) < 1:
@@ -724,7 +631,7 @@
                     except Exception:
                         warn('xml_id_syntax_error', word, '/'.join(field))
                         continue
-                    db_id = ir_model_data_obj.get_id(module, xml_id)
+                    db_id = ModelData.get_id(module, xml_id)
                     res_ids.append(db_id)
                 if ftype == 'many2many' and res_ids:
                     return [('set', res_ids)]
@@ -732,7 +639,7 @@
                     return '%s,%s' % (relation, str(res_ids[0]))
                 return res_ids and res_ids[0] or False
 
-            line = datas[position]
+            line = data[position]
             row = {}
             translate = {}
             todo = set()
@@ -795,7 +702,7 @@
             for field in todo:
                 newfd = pool.get(fields_def[field]['relation']
                         ).fields_get()
-                res = process_lines(self, datas, prefix + [field], newfd,
+                res = process_lines(data, prefix + [field], newfd,
                         position)
                 (newrow, max2, _) = res
                 nbrmax = max(nbrmax, max2)
@@ -803,17 +710,17 @@
                 row[field] = (reduce(lambda x, y: x or y, newrow.values()) and
                          [('create', newrow)]) or []
                 i = max2
-                while (position + i) < len(datas):
+                while (position + i) < len(data):
                     test = True
                     for j, field2 in enumerate(fields_names):
                         if len(field2) <= (prefix_len + 1) \
-                               and datas[position + i][j]:
+                               and data[position + i][j]:
                             test = False
                             break
                     if not test:
                         break
                     (newrow, max2, _) = \
-                            process_lines(self, datas, prefix + [field], newfd,
+                            process_lines(data, prefix + [field], newfd,
                                     position + i)
                     if reduce(lambda x, y: x or y, newrow.values()):
                         row[field].append(('create', newrow))
@@ -821,10 +728,10 @@
                     nbrmax = max(nbrmax, i)
             if prefix_len == 0:
                 for i in xrange(max(nbrmax, 1)):
-                    datas.pop(0)
+                    data.pop(0)
             return (row, nbrmax, translate)
 
-        ir_model_data_obj = pool.get('ir.model.data')
+        ModelData = pool.get('ir.model.data')
 
         # logger for collecting warnings for the client
         warnings = logging.Logger("import")
@@ -832,27 +739,26 @@
         warnings.addHandler(logging.StreamHandler(warning_stream))
 
         len_fields_names = len(fields_names)
-        for data in datas:
-            assert len(data) == 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 = self.fields_get()
+        fields_def = cls.fields_get()
         done = 0
 
         warning = ''
-        while len(datas):
+        while len(data):
             res = {}
             try:
                 (res, _, translate) = \
-                        process_lines(self, datas, [], fields_def)
+                        process_lines(data, [], fields_def)
                 warning = warning_stream.getvalue()
                 if warning:
                     # XXX should raise Exception
                     Transaction().cursor.rollback()
                     return (-1, res, warning, '')
-                new_id = self.create(res)
+                new_id = cls.create(res)
                 for lang in translate:
                     with Transaction().set_context(language=lang):
-                        self.write(new_id, translate[lang])
+                        cls.write(new_id, translate[lang])
             except Exception, exp:
                 logger = logging.getLogger('import')
                 logger.error(exp)
@@ -864,7 +770,8 @@
             done += 1
         return (done, 0, 0, 0)
 
-    def check_xml_record(self, ids, values):
+    @classmethod
+    def check_xml_record(cls, records, values):
         """
         Check if a list of records and their corresponding fields are
         originating from xml data. This is used by write and delete
@@ -874,28 +781,21 @@
         xml. Values is the dictionary of written values. If values is
         equal to None, no field by field check is performed, False is
         returned as soon as one of the record comes from the xml.
-
-        :param ids: a list of ids or an id
-        :param values: a dictionary with field names as key and
-            written values as value
-        :return: True or False
         """
-        model_data_obj = Pool().get('ir.model.data')
+        ModelData = Pool().get('ir.model.data')
         # Allow root user to update/delete
         if Transaction().user == 0:
             return True
-        if isinstance(ids, (int, long)):
-            ids = [ids]
         with Transaction().set_user(0):
-            model_data_ids = model_data_obj.search([
-                ('model', '=', self._name),
-                ('db_id', 'in', ids),
+            model_data_ids = ModelData.search([
+                ('model', '=', cls.__name__),
+                ('db_id', 'in', map(int, records)),
                 ])
             if not model_data_ids:
                 return True
             if values == None:
                 return False
-            for line in model_data_obj.browse(model_data_ids):
+            for line in ModelData.browse(model_data_ids):
                 if not line.values:
                     continue
                 xml_values = safe_eval(line.values, {
@@ -907,84 +807,89 @@
                         return False
         return True
 
-    def check_recursion(self, ids, parent='parent'):
+    @classmethod
+    def check_recursion(cls, records, parent='parent'):
         '''
         Function that checks if there is no recursion in the tree
         composed with parent as parent field name.
-
-        :param ids: a list of ids
-        :param parent: the parent field name
-        :return: True or False
         '''
-        if parent in self._columns:
-            parent_type = self._columns[parent]._type
-        elif parent in self._inherit_fields:
-            parent_type = self._inherit_fields[parent][2]._type
+        if parent in cls._fields:
+            parent_type = cls._fields[parent]._type
+        elif parent in cls._inherit_fields:
+            parent_type = cls._inherit_fields[parent][2]._type
         else:
             raise Exception('Field %s not available on object "%s"' % \
-                    (parent, self._name))
+                    (parent, cls.__name__))
 
         if parent_type not in ('many2one', 'many2many'):
             raise Exception(
                     'Unsupported field type "%s" for field "%s" on "%s"' % \
-                    (parent_type, parent, self._name))
+                    (parent_type, parent, cls.__name__))
 
-        records = self.browse(ids)
         visited = set()
 
         for record in records:
             walked = set()
-            walker = record[parent]
+            walker = getattr(record, parent)
             while walker:
                 if parent_type == 'many2many':
                     for walk in walker:
                         walked.add(walk.id)
                         if walk.id == record.id:
                             return False
-                    walker = list(chain(*(walk[parent] for walk in walker
-                            if walk.id not in visited)))
+                    walker = list(chain(*(getattr(walk, parent)
+                                for walk in walker if walk.id not in visited)))
                 else:
                     walked.add(walker.id)
                     if walker.id == record.id:
                         return False
-                    walker = walker[parent] not in visited and walker[parent]
+                    walker = (getattr(walker, parent) not in visited
+                        and getattr(walker, parent))
             visited.update(walked)
 
         return True
 
-    def _get_error_args(self, field_name):
+    @classmethod
+    def _get_error_args(cls, field_name):
         pool = Pool()
-        model_field_obj = pool.get('ir.model.field')
-        error_args = (field_name, self._name)
-        if model_field_obj:
-            model_field_ids = model_field_obj.search([
+        ModelField = pool.get('ir.model.field')
+        error_args = (field_name, cls.__name__)
+        if ModelField:
+            model_fields = ModelField.search([
                         ('name', '=', field_name),
-                        ('model.model', '=', self._name),
+                        ('model.model', '=', cls.__name__),
                         ], limit=1)
-            if model_field_ids:
-                model_field = model_field_obj.browse(model_field_ids[0])
+            if model_fields:
+                model_field, = model_fields
                 error_args = (model_field.field_description,
                         model_field.model.name)
         return error_args
 
-    def _validate(self, ids):
+    @classmethod
+    def _validate(cls, records):
         pool = Pool()
         if (Transaction().user == 0
                 and Transaction().context.get('user')):
             with Transaction().set_user(Transaction().context.get('user')):
-                return self._validate(ids)
+                return cls._validate(cls.browse(records))
 
-        for field in self._constraints:
-            if not getattr(self, field[0])(ids):
-                self.raise_user_error(field[1])
+        def call(name):
+            method = getattr(cls, name)
+            if not hasattr(method, 'im_self') or method.im_self:
+                return method(records)
+            else:
+                return all(method(r) for r in records)
+        for field in cls._constraints:
+            if not call(field[0]):
+                cls.raise_user_error(field[1])
 
         if not 'res.user' in pool.object_name_list() \
                 or Transaction().user == 0:
             ctx_pref = {
             }
         else:
-            user_obj = pool.get('res.user')
-            ctx_pref = user_obj.get_preferences(context_only=True)
+            User = pool.get('res.user')
+            ctx_pref = User.get_preferences(context_only=True)
 
         def is_pyson(test):
             if isinstance(test, PYSON):
@@ -999,8 +904,7 @@
             return False
 
         with Transaction().set_context(ctx_pref):
-            records = self.browse(ids)
-            for field_name, field in self._columns.iteritems():
+            for field_name, field in cls._fields.iteritems():
                 if isinstance(field, fields.Function) and \
                         not field.setter:
                     continue
@@ -1009,13 +913,13 @@
                         ('many2one', 'many2many', 'one2many', 'one2one')
                     and field.domain):
                     if field._type in ('many2one', 'one2many'):
-                        relation_obj = pool.get(field.model_name)
+                        Relation = pool.get(field.model_name)
                     else:
-                        relation_obj = field.get_target()
+                        Relation = field.get_target()
                     if is_pyson(field.domain):
                         pyson_domain = PYSONEncoder().encode(field.domain)
                         for record in records:
-                            env = EvalEnvironment(record, self)
+                            env = EvalEnvironment(record, cls)
                             env.update(Transaction().context)
                             env['current_date'] = datetime.datetime.today()
                             env['time'] = time
@@ -1023,56 +927,60 @@
                             env['active_id'] = record.id
                             domain = PYSONDecoder(env).decode(pyson_domain)
                             relation_ids = []
-                            if record[field_name]:
+                            if getattr(record, field_name):
                                 if field._type in ('many2one',):
-                                    relation_ids.append(record[field_name].id)
+                                    relation_ids.append(
+                                        getattr(record, field_name).id)
                                 else:
                                     relation_ids.extend(
-                                            [x.id for x in record[field_name]])
-                            if relation_ids and not relation_obj.search([
+                                        [x.id for x in getattr(record,
+                                                field_name)])
+                            if relation_ids and not Relation.search([
                                         'AND',
                                         [('id', 'in', relation_ids)],
                                         domain,
                                         ]):
-                                self.raise_user_error(
+                                cls.raise_user_error(
                                         'domain_validation_record',
-                                        error_args=self._get_error_args(
+                                        error_args=cls._get_error_args(
                                             field_name))
                     else:
                         relation_ids = []
                         for record in records:
-                            if record[field_name]:
+                            if getattr(record, field_name):
                                 if field._type in ('many2one',):
-                                    relation_ids.append(record[field_name].id)
+                                    relation_ids.append(
+                                        getattr(record, field_name).id)
                                 else:
                                     relation_ids.extend(
-                                            [x.id for x in record[field_name]])
+                                        [x.id for x in getattr(record,
+                                                field_name)])
                         if relation_ids:
-                            find_ids = relation_obj.search([
+                            finds = Relation.search([
                                 'AND',
                                 [('id', 'in', relation_ids)],
                                 field.domain,
                                 ])
+                            find_ids = map(int, finds)
                             if not set(relation_ids) == set(find_ids):
-                                self.raise_user_error(
+                                cls.raise_user_error(
                                         'domain_validation_record',
-                                        error_args=self._get_error_args(
+                                        error_args=cls._get_error_args(
                                             field_name))
 
                 def required_test(value, field_name):
-                    if (isinstance(value,
-                                (BrowseRecordNull, type(None), type(False),
-                                    BrowseRecordList, basestring))
+                    if (isinstance(value, (type(None), type(False), list,
+                                    tuple, basestring))
                             and not value):
-                        self.raise_user_error('required_validation_record',
-                            error_args=self._get_error_args(field_name))
+                        cls.raise_user_error('required_validation_record',
+                            error_args=cls._get_error_args(field_name))
                 # validate states required
                 if field.states and 'required' in field.states:
                     if is_pyson(field.states['required']):
                         pyson_required = PYSONEncoder().encode(
                                 field.states['required'])
                         for record in records:
-                            env = EvalEnvironment(record, self)
+                            env = EvalEnvironment(record, cls)
                             env.update(Transaction().context)
                             env['current_date'] = datetime.datetime.today()
                             env['time'] = time
@@ -1080,21 +988,23 @@
                             env['active_id'] = record.id
                             required = PYSONDecoder(env).decode(pyson_required)
                             if required:
-                                required_test(record[field_name], field_name)
+                                required_test(getattr(record, field_name),
+                                    field_name)
                     else:
                         if field.states['required']:
                             for record in records:
-                                required_test(record[field_name], field_name)
+                                required_test(getattr(record, field_name),
+                                    field_name)
                 # validate required
                 if field.required:
                     for record in records:
-                        required_test(record[field_name], field_name)
+                        required_test(getattr(record, field_name), field_name)
                 # validate size
                 if hasattr(field, 'size') and field.size is not None:
                     for record in records:
                         if isinstance(field.size, PYSON):
                             pyson_size = PYSONEncoder().encode(field.size)
-                            env = EvalEnvironment(record, self)
+                            env = EvalEnvironment(record, cls)
                             env.update(Transaction().context)
                             env['current_date'] = datetime.datetime.today()
                             env['time'] = time
@@ -1103,15 +1013,16 @@
                             field_size = PYSONDecoder(env).decode(pyson_size)
                         else:
                             field_size = field.size
-                        if (len(record[field_name] or '') > field_size >= 0):
-                            self.raise_user_error(
+                        if (len(getattr(record, field_name) or '')
+                                > field_size >= 0):
+                            cls.raise_user_error(
                                 'size_validation_record',
-                                error_args=self._get_error_args(field_name))
+                                error_args=cls._get_error_args(field_name))
 
                 def digits_test(value, digits, field_name):
                     def raise_user_error():
-                        self.raise_user_error('digits_validation_record',
-                            error_args=self._get_error_args(field_name))
+                        cls.raise_user_error('digits_validation_record',
+                            error_args=cls._get_error_args(field_name))
                     if value is None:
                         return
                     if isinstance(value, Decimal):
@@ -1126,18 +1037,19 @@
                     if is_pyson(field.digits):
                         pyson_digits = PYSONEncoder().encode(field.digits)
                         for record in records:
-                            env = EvalEnvironment(record, self)
+                            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
                             digits = PYSONDecoder(env).decode(pyson_digits)
-                            digits_test(record[field_name], digits, field_name)
+                            digits_test(getattr(record, field_name), digits,
+                                field_name)
                     else:
                         for record in records:
-                            digits_test(record[field_name], field.digits,
-                                field_name)
+                            digits_test(getattr(record, field_name),
+                                field.digits, field_name)
 
                 def format_test(value, format, field_name):
                     if not value:
@@ -1146,8 +1058,8 @@
                         value = value.time()
                     if value != datetime.datetime.strptime(
                             value.strftime(format), format).time():
-                        self.raise_user_error('time_format_validation_record',
-                            error_args=self._get_error_args(field_name))
+                        cls.raise_user_error('time_format_validation_record',
+                            error_args=cls._get_error_args(field_name))
 
                 # validate time format
                 if (field._type in ('datetime', 'time')
@@ -1155,25 +1067,27 @@
                     if is_pyson(field.format):
                         pyson_format = PYSONDecoder().encode(field.format)
                         for record in records:
-                            env = EvalEnvironment(record, self)
+                            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
                             format = PYSONDecoder(env).decode(pyson_format)
-                            format_test(record[field_name], format, field_name)
+                            format_test(getattr(record, field_name), format,
+                                field_name)
                     else:
                         for record in records:
-                            format_test(record[field_name], field.format,
-                                field_name)
+                            format_test(getattr(record, field_name),
+                                field.format, field_name)
 
-    def _clean_defaults(self, defaults):
+    @classmethod
+    def _clean_defaults(cls, defaults):
         pool = Pool()
         vals = {}
         for field in defaults.keys():
-            fld_def = (field in self._columns) and self._columns[field] \
-                    or self._inherit_fields[field][2]
+            fld_def = (field in cls._fields) and cls._fields[field] \
+                    or cls._inherit_fields[field][2]
             if fld_def._type in ('many2one', 'one2one'):
                 if isinstance(defaults[field], (list, tuple)):
                     vals[field] = defaults[field][0]
@@ -1192,3 +1106,287 @@
             else:
                 vals[field] = defaults[field]
         return vals
+
+    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 _ids is not None:
+            self._ids = _ids
+            assert id in _ids
+        else:
+            self._ids = [self.id]
+
+        self._cursor_cache = self._cursor.get_cache(self._context)
+
+        if _local_cache is not None:
+            self._local_cache = _local_cache
+        else:
+            self._local_cache = LRUDict(RECORD_CACHE_SIZE)
+        self._local_counter = Transaction().counter
+
+    @property
+    def _cache(self):
+        cache = self._cursor_cache
+        if self.__name__ not in cache:
+            cache[self.__name__] = LRUDict(RECORD_CACHE_SIZE)
+        return cache[self.__name__]
+
+    def __getattr__(self, name):
+        try:
+            return super(ModelStorage, self).__getattr__(name)
+        except AttributeError:
+            if self.id < 0:
+                raise
+
+        counter = Transaction().counter
+        if self._local_counter != counter:
+            self._local_cache.clear()
+            self._local_counter = counter
+        try:
+            return self._local_cache[self.id][name]
+        except KeyError:
+            pass
+        try:
+            return self._cache[self.id][name]
+        except KeyError:
+            pass
+
+        # fetch the definition of the field
+        if name in self._fields:
+            field = self._fields[name]
+        elif name in self._inherit_fields:
+            field = self._inherit_fields[name][2]
+        else:
+            raise AttributeError("'%s' Model has no attribute '%s'"
+                % (self.__name__, name))
+
+        # build the list of fields we will fetch
+        ffields = {
+            name: field,
+            }
+        if field.loading == 'eager':
+            FieldAccess = Pool().get('ir.model.field.access')
+            fread_accesses = {}
+            for inherit_name in self._inherits:
+                Inherit = Pool().get(inherit_name)
+                fread_accesses.update(FieldAccess.check(inherit_name,
+                    Inherit._fields.keys(), 'read', access=True))
+            fread_accesses.update(FieldAccess.check(self.__name__,
+                self._fields.keys(), 'read', access=True))
+            to_remove = set(x for x, y in fread_accesses.iteritems()
+                    if not y and x != name)
+
+            threshold = BROWSE_FIELD_TRESHOLD
+            inherit_threshold = threshold - len(self._fields)
+
+            def not_cached(item):
+                fname, field = item
+                return (fname not in self._cache.get(self.id, {})
+                    and fname not in self._local_cache.get(self.id, {}))
+
+            def to_load(item):
+                fname, field = item
+                return (field.loading == 'eager'
+                    and fname not in to_remove)
+
+            def overrided(item):
+                fname, field = item
+                return fname in self._fields
+
+            if inherit_threshold > 0:
+                ifields = ((fname, field)
+                    for fname, (_, _, field) in
+                    self._inherit_fields.iteritems())
+                ifields = ifilterfalse(overrided,
+                    ifilter(to_load,
+                        ifilter(not_cached, ifields)))
+                ifields = islice(ifields, 0, inherit_threshold)
+                ffields.update(ifields)
+                threshold -= inherit_threshold
+
+            ifields = ifilter(to_load,
+                ifilter(not_cached,
+                    self._fields.iteritems()))
+            ifields = islice(ifields, 0, threshold)
+            ffields.update(ifields)
+
+        # add datetime_field
+        for field in ffields.values():
+            if hasattr(field, 'datetime_field') and field.datetime_field:
+                if field.datetime_field in self._fields:
+                    datetime_field = self._fields[field.datetime_field]
+                else:
+                    datetime_field = self._inherit_fields[
+                            field.datetime_field][2]
+                ffields[field.datetime_field] = datetime_field
+
+        def filter_(id_):
+            try:
+                if name in self._local_cache[id_]:
+                    return False
+                if name in self._cache[id_]:
+                    return False
+            except KeyError:
+                pass
+            return True
+        index = self._ids.index(self.id)
+        ids = chain(islice(self._ids, index, None),
+            islice(self._ids, 0, max(index - 1, 0)))
+        ids = list(islice(ifilter(filter_, ids), self._cursor.IN_MAX))
+
+        def instantiate(field, value, data):
+            if field._type in ('many2one', 'one2one', 'reference'):
+                if value is None or value is False:
+                    return None
+            elif field._type in ('one2many', 'many2many'):
+                if not value:
+                    return ()
+            try:
+                if field._type == 'reference':
+                    model_name, record_id = value.split(',')
+                    Model = Pool().get(model_name)
+                    try:
+                        value = int(record_id)
+                    except ValueError:
+                        return value
+                else:
+                    Model = field.get_target()
+            except KeyError:
+                return value
+            if getattr(field, 'datetime_field', None):
+                _datetime = data.get(field.datetime_field)
+            else:
+                _datetime = None
+            with Transaction().set_context(_datetime=_datetime):
+                local_cache = model2cache.setdefault(Model,
+                    LRUDict(RECORD_CACHE_SIZE))
+                ids = model2ids.setdefault(Model, [])
+                if field._type in ('many2one', 'one2one', 'reference'):
+                    ids.append(value)
+                    return Model(value, _ids=ids, _local_cache=local_cache)
+                elif field._type in ('one2many', 'many2many'):
+                    ids.extend(value)
+                    return tuple(Model(id, _ids=ids, _local_cache=local_cache)
+                        for id in value)
+
+        model2ids = {}
+        model2cache = {}
+        # Read the data
+        with contextlib.nested(Transaction().set_cursor(self._cursor),
+                Transaction().set_user(self._user),
+                Transaction().set_context(self._context)):
+            # create browse records for 'remote' models
+            for data in self.read(ids, ffields.keys()):
+                for fname, field in ffields.iteritems():
+                    if field._type in ('many2one', 'one2one', 'one2many',
+                            'many2many', 'reference'):
+                        data[fname] = instantiate(field, data[fname], data)
+                    elif not isinstance(field, fields.Function):
+                        continue
+                    if data['id'] == self.id and fname == name:
+                        value = data[fname]
+                    if data['id'] not in self._local_cache:
+                        self._local_cache[data['id']] = {}
+                    self._local_cache[data['id']][fname] = data[fname]
+                    del data[fname]
+                if data['id'] not in self._cache:
+                    self._cache[data['id']] = {}
+                self._cache[data['id']].update(data)
+                if data['id'] == self.id and name in data:
+                    value = data[name]
+        return value
+
+    @property
+    def _save_values(self):
+        values = {}
+        if not self._values:
+            return values
+        for fname, value in self._values.iteritems():
+            if fname in self._fields:
+                field = self._fields[fname]
+            else:
+                field = self._inherit_fields[fname][2]
+            if field._type in ('many2one', 'one2one', 'reference'):
+                if value:
+                    if value.id < 0 and field._type != 'reference':
+                        value.save()
+                    if field._type == 'reference':
+                        value = str(value)
+                    else:
+                        value = value.id
+            if field._type in ('one2many', 'many2many'):
+                targets = value
+                value = [
+                    ('set', []),
+                    ]
+                for target in targets:
+                    if target.id < 0:
+                        value.append(('create', target._save_values))
+                    elif target._save_values:
+                        value.append(
+                            ('write', [target.id], target._save_values))
+                    else:
+                        value[0][1].append(target.id)
+            values[fname] = value
+        return values
+
+    def save(self):
+        if self._save_values or self.id < 0:
+            with contextlib.nested(Transaction().set_cursor(self._cursor),
+                    Transaction().set_user(self._user),
+                    Transaction().set_context(self._context)):
+                if self.id < 0:
+                    self._ids.remove(self.id)
+                    self.id = self.create(self._save_values).id
+                    self._ids.append(self.id)
+                else:
+                    self.write([self], self._save_values)
+        self._values = None
+
+
+class EvalEnvironment(dict):
+
+    def __init__(self, record, Model):
+        super(EvalEnvironment, self).__init__()
+        self._record = record
+        self._model = Model
+
+    def __getitem__(self, item):
+        if item.startswith('_parent_'):
+            field = item[8:]
+            if field in self._model._fields:
+                model_name = self._model._fields[field].model_name
+            else:
+                model_name = self._model._inherit_fields[field][2].model_name
+            ParentModel = Pool().get(model_name)
+            return EvalEnvironment(getattr(self._record, field), ParentModel)
+        if (item in self._model._fields
+                or item in self._model._inherit_fields):
+            value = getattr(self._record, item)
+            if isinstance(value, Model):
+                return value.id
+            elif isinstance(value, list):
+                return [r.id for r in value]
+            else:
+                return value
+        return super(EvalEnvironment, self).__getitem__(item)
+
+    def __getattr__(self, item):
+        try:
+            return self.__getitem__(item)
+        except KeyError, exception:
+            raise AttributeError(*exception.args)
+
+    def get(self, item, default=None):
+        try:
+            return self.__getitem__(item)
+        except Exception:
+            pass
+        return super(EvalEnvironment, self).get(item, default)
+
+    def __nonzero__(self):
+        return bool(self._record)
diff -r fb51f5b4aa4c trytond/model/modelview.py
--- a/trytond/model/modelview.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/modelview.py	Mon Sep 24 20:36:36 2012 +0200
@@ -3,12 +3,15 @@
 from lxml import etree
 from functools import wraps
 from trytond.model import Model
-from trytond.tools import safe_eval
+from trytond.tools import safe_eval, ClassProperty
 from trytond.pyson import PYSONEncoder, CONTEXT
 from trytond.transaction import Transaction
 from trytond.cache import Cache
 from trytond.pool import Pool
 from trytond.exceptions import UserError
+from trytond.rpc import RPC
+
+__all__ = ['ModelView']
 
 
 def _find(tree, element):
@@ -74,12 +77,16 @@
     Define a model with views in Tryton.
     """
     __modules_list = None  # Cache for the modules list sorted by dependency
+    _fields_view_get_cache = Cache('modelview.fields_view_get')
+    _view_toolbar_get_cache = Cache('modelview.view_toolbar_get')
 
     @staticmethod
     def _reset_modules_list():
         ModelView.__modules_list = None
 
-    def _get_modules_list(self):
+    @ClassProperty
+    @classmethod
+    def _modules_list(cls):
         from trytond.modules import create_graph, get_module_list
         if ModelView.__modules_list:
             return ModelView.__modules_list
@@ -87,27 +94,28 @@
         ModelView.__modules_list = [x.name for x in graph] + [None]
         return ModelView.__modules_list
 
-    _modules_list = property(fget=_get_modules_list)
+    @classmethod
+    def __setup__(cls):
+        super(ModelView, cls).__setup__()
+        cls.__rpc__['fields_view_get'] = RPC()
+        cls.__rpc__['view_toolbar_get'] = RPC()
+        cls._buttons = {}
 
-    def __init__(self):
-        super(ModelView, self).__init__()
-        self._rpc['fields_view_get'] = False
-        self._rpc['view_toolbar_get'] = False
-        self._buttons = {}
-
-    @Cache('modelview.fields_view_get')
-    def fields_view_get(self, view_id=None, view_type='form'):
+    @classmethod
+    def fields_view_get(cls, view_id=None, view_type='form'):
         '''
         Return a view definition.
-
-        :param view_id: the id of the view, if None the first one will be used
-        :param view_type: the type of the view if view_id is None
-        :return: a dictionary with keys:
+        If view_id is None the first one will be used of view_type.
+        The definition is a dictionary with keys:
            - model: the model name
            - arch: the xml description of the view
            - fields: a dictionary with the definition of each field in the view
         '''
-        result = {'model': self._name}
+        key = (cls.__name__, view_id, view_type)
+        result = cls._fields_view_get_cache.get(key)
+        if result:
+            return result
+        result = {'model': cls.__name__}
         pool = Pool()
 
         test = True
@@ -117,7 +125,7 @@
         cursor = Transaction().cursor
         while test:
             if view_id:
-                where = (model and (" and model='%s'" % (self._name,))) or ''
+                where = (model and (" and model='%s'" % (cls.__name__,))) or ''
                 cursor.execute('SELECT arch, field_childs, id, type, ' \
                             'inherit, model ' \
                         'FROM ir_ui_view WHERE id = %s ' + where, (view_id,))
@@ -127,7 +135,7 @@
                         'FROM ir_ui_view ' \
                         'WHERE model = %s AND type = %s ' \
                         'ORDER BY inherit DESC, priority ASC, id ASC',
-                        (self._name, view_type))
+                        (cls.__name__, view_type))
             sql_res = cursor.fetchone()
             if not sql_res:
                 break
@@ -145,9 +153,9 @@
             result['field_childs'] = sql_res[1] or False
 
             # Check if view is not from an inherited model
-            if sql_res[5] != self._name:
-                inherit_obj = pool.get(sql_res[5])
-                result['arch'] = inherit_obj.fields_view_get(
+            if sql_res[5] != cls.__name__:
+                Inherit = pool.get(sql_res[5])
+                result['arch'] = Inherit.fields_view_get(
                         result['view_id'])['arch']
                 view_id = inherit_view_id
 
@@ -156,13 +164,13 @@
                     'WHERE (inherit = %s AND model = %s) OR ' \
                         ' (id = %s AND inherit IS NOT NULL) '
                     'ORDER BY priority ASC, id ASC',
-                    (view_id, self._name, view_id))
+                    (view_id, cls.__name__, view_id))
             sql_inherit = cursor.fetchall()
             raise_p = False
             while True:
                 try:
                     sql_inherit.sort(key=lambda x:
-                        self._modules_list.index(x[2] or None))
+                        cls._modules_list.index(x[2] or None))
                     break
                 except ValueError:
                     if raise_p:
@@ -182,9 +190,9 @@
         # otherwise, build some kind of default view
         else:
             if view_type == 'form':
-                res = self.fields_get()
+                res = cls.fields_get()
                 xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
-                '''<form string="%s" col="4">''' % (self._description,)
+                '''<form string="%s" col="4">''' % (cls.__doc__,)
                 for i in res:
                     if i in ('create_uid', 'create_date',
                             'write_uid', 'write_date', 'id', 'rec_name'):
@@ -199,11 +207,11 @@
                 xml += "</form>"
             elif view_type == 'tree':
                 field = 'id'
-                if self._rec_name in self._columns:
-                    field = self._rec_name
+                if cls._rec_name in cls._fields:
+                    field = cls._rec_name
                 xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
                 '''<tree string="%s"><field name="%s"/></tree>''' \
-                % (self._description, field)
+                % (cls.__doc__, field)
             else:
                 xml = ''
             result['type'] = view_type
@@ -214,34 +222,41 @@
         # Update arch and compute fields from arch
         parser = etree.XMLParser(remove_blank_text=True)
         tree = etree.fromstring(result['arch'], parser)
-        xarch, xfields = self._view_look_dom_arch(tree, result['type'],
+        xarch, xfields = cls._view_look_dom_arch(tree, result['type'],
                 result['field_childs'])
         result['arch'] = xarch
         result['fields'] = xfields
 
+        cls._fields_view_get_cache.set(key, result)
         return result
 
-    @Cache('modelview.view_toolbar_get')
-    def view_toolbar_get(self):
+    @classmethod
+    def view_toolbar_get(cls):
         """
         Returns the model specific actions.
-
-        :return: a dictionary with keys:
+        A dictionary with keys:
             - print: a list of available reports
             - action: a list of available actions
             - relate: a list of available relations
         """
-        action_obj = Pool().get('ir.action.keyword')
-        prints = action_obj.get_keyword('form_print', (self._name, -1))
-        actions = action_obj.get_keyword('form_action', (self._name, -1))
-        relates = action_obj.get_keyword('form_relate', (self._name, -1))
-        return {
+        Action = Pool().get('ir.action.keyword')
+        key = (cls.__name__, repr(Transaction().context))
+        result = cls._view_toolbar_get_cache.get(key)
+        if result:
+            return result
+        prints = Action.get_keyword('form_print', (cls.__name__, -1))
+        actions = Action.get_keyword('form_action', (cls.__name__, -1))
+        relates = Action.get_keyword('form_relate', (cls.__name__, -1))
+        result = {
             'print': prints,
             'action': actions,
             'relate': relates,
-        }
+            }
+        cls._view_toolbar_get_cache.set(key, result)
+        return result
 
-    def view_header_get(self, value, view_type='form'):
+    @classmethod
+    def view_header_get(cls, value, view_type='form'):
         """
         Overload this method if you need a window title.
         which depends on the context
@@ -252,30 +267,31 @@
         """
         return value
 
-    def _view_look_dom_arch(self, tree, type, field_children=None):
+    @classmethod
+    def _view_look_dom_arch(cls, tree, type, field_children=None):
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
-        field_access_obj = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
+        FieldAccess = pool.get('ir.model.field.access')
 
         fields_width = {}
         tree_root = tree.getroottree().getroot()
 
         # Find field without read access
-        fread_accesses = field_access_obj.check(self._name,
-                self._columns.keys(), 'read', access=True)
+        fread_accesses = FieldAccess.check(cls.__name__,
+                cls._fields.keys(), 'read', access=True)
         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 model_access_obj.check(field.model_name, mode='read',
+                if not ModelAccess.check(field.model_name, mode='read',
                         raise_exception=False):
                     return False
             if field._type in ('many2many', 'one2one'):
-                if not model_access_obj.check(field.target, mode='read',
+                if not ModelAccess.check(field.target, mode='read',
                         raise_exception=False):
                     return False
-                elif not model_access_obj.check(field.relation_name,
+                elif not ModelAccess.check(field.relation_name,
                         mode='read', raise_exception=False):
                     return False
             if field._type == 'reference':
@@ -283,35 +299,35 @@
                 if isinstance(selection, basestring):
                     selection = getattr(model, field.selection)()
                 for model_name, _ in selection:
-                    if not model_access_obj.check(model_name, mode='read',
+                    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 self._columns.iteritems():
-            if not check_relation(self, field):
+        for name, field in cls._fields.iteritems():
+            if not check_relation(cls, field):
                 fields_to_remove.append(name)
 
-        for name, field in self._columns.iteritems():
+        for name, field in cls._fields.iteritems():
             for field_to_remove in fields_to_remove:
                 if field_to_remove in field.depends:
                     fields_to_remove.append(name)
 
         # Find field inherited without read access
-        for inherit_name in self._inherits:
-            inherit_obj = pool.get(inherit_name)
-            fread_accesses = field_access_obj.check(inherit_obj._name,
-                    inherit_obj._columns.keys(), 'read', access=True)
+        for inherit_name in cls._inherits:
+            Inherit = pool.get(inherit_name)
+            fread_accesses = FieldAccess.check(Inherit.__name__,
+                    Inherit._fields.keys(), 'read', access=True)
             fields_to_remove += list(x for x, y in fread_accesses.iteritems()
-                    if not y and x not in self._columns.keys())
+                    if not y and x not in cls._fields.keys())
 
             # Find relation field without read access
-            for name, field in inherit_obj._columns.iteritems():
-                if not check_relation(inherit_obj, field):
+            for name, field in Inherit._fields.iteritems():
+                if not check_relation(Inherit, field):
                     fields_to_remove.append(name)
 
-            for name, field in inherit_obj._columns.iteritems():
+            for name, field in Inherit._fields.iteritems():
                 for field_to_remove in fields_to_remove:
                     if field_to_remove in field.depends:
                         fields_to_remove.append(name)
@@ -329,55 +345,56 @@
                     parent.remove(element)
 
         if type == 'tree':
-            viewtreewidth_obj = pool.get('ir.ui.view_tree_width')
-            viewtreewidth_ids = viewtreewidth_obj.search([
-                ('model', '=', self._name),
+            ViewTreeWidth = pool.get('ir.ui.view_tree_width')
+            viewtreewidth_ids = ViewTreeWidth.search([
+                ('model', '=', cls.__name__),
                 ('user', '=', Transaction().user),
                 ])
-            for viewtreewidth in viewtreewidth_obj.browse(viewtreewidth_ids):
+            for viewtreewidth in ViewTreeWidth.browse(viewtreewidth_ids):
                 if viewtreewidth.width > 0:
                     fields_width[viewtreewidth.field] = viewtreewidth.width
 
-        fields_def = self.__view_look_dom(tree_root, type,
+        fields_def = cls.__view_look_dom(tree_root, type,
                 fields_width=fields_width)
 
         if field_children:
             fields_def.setdefault(field_children, {'name': field_children})
             model, field = None, None
-            if field_children in self._columns:
-                model = self
-                field = self._columns[field_children]
-            elif field_children in self._inherit_fields:
-                model_name, model, field = self._inherit_fields[field_children]
-            if model and field and field.model_name == model._name:
+            if field_children in cls._fields:
+                model = cls
+                field = cls._fields[field_children]
+            elif field_children in cls._inherit_fields:
+                model_name, model, field = cls._inherit_fields[field_children]
+            if model and field and field.model_name == model.__name__:
                 fields_def.setdefault(field.field, {'name': field.field})
 
         for field_name in fields_def.keys():
-            if field_name in self._columns:
-                field = self._columns[field_name]
-            elif field_name in self._inherit_fields:
-                field = self._inherit_fields[field_name][2]
+            if field_name in cls._fields:
+                field = cls._fields[field_name]
+            elif field_name in cls._inherit_fields:
+                field = cls._inherit_fields[field_name][2]
             else:
                 continue
             for depend in field.depends:
                 fields_def.setdefault(depend, {'name': depend})
 
-        if ('active' in self._columns) or ('active' in self._inherit_fields):
+        if ('active' in cls._fields) or ('active' in cls._inherit_fields):
             fields_def.setdefault('active', {'name': 'active'})
 
         arch = etree.tostring(tree, encoding='utf-8', pretty_print=False)
-        fields2 = self.fields_get(fields_def.keys())
+        fields2 = cls.fields_get(fields_def.keys())
         for field in fields_def:
             if field in fields2:
                 fields2[field].update(fields_def[field])
         return arch, fields2
 
-    def __view_look_dom(self, element, type, fields_width=None):
+    @classmethod
+    def __view_look_dom(cls, element, type, fields_width=None):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        model_data_obj = pool.get('ir.model.data')
-        button_obj = pool.get('ir.model.button')
-        user_obj = pool.get('res.user')
+        Translation = pool.get('ir.translation')
+        ModelData = pool.get('ir.model.data')
+        Button = pool.get('ir.model.button')
+        User = pool.get('res.user')
 
         if fields_width is None:
             fields_width = {}
@@ -389,15 +406,15 @@
                 if element.get(attr):
                     attrs = {}
                     try:
-                        if element.get(attr) in self._columns:
-                            field = self._columns[element.get(attr)]
+                        if element.get(attr) in cls._fields:
+                            field = cls._fields[element.get(attr)]
                         else:
-                            field = self._inherit_fields[element.get(
+                            field = cls._inherit_fields[element.get(
                                 attr)][2]
                         if hasattr(field, 'model_name'):
                             relation = field.model_name
                         else:
-                            relation = field.get_target()._name
+                            relation = field.get_target().__name__
                     except Exception:
                         relation = False
                     if relation and element.tag == 'field':
@@ -411,9 +428,9 @@
                                 try:
                                     view_ids.append(int(view_id))
                                 except ValueError:
-                                    view_ids.append(model_data_obj.get_id(
+                                    view_ids.append(ModelData.get_id(
                                             *view_id.split('.')))
-                        relation_obj = pool.get(relation)
+                        Relation = pool.get(relation)
                         if (not len(element)
                                 and type == 'form'
                                 and field._type in ('one2many', 'many2many')):
@@ -421,14 +438,14 @@
                             # loop
                             if view_ids:
                                 for view_id in view_ids:
-                                    view = relation_obj.fields_view_get(
+                                    view = Relation.fields_view_get(
                                         view_id=view_id)
                                     views[view['type']] = view
                                     break
                             else:
                                 for view_type in mode:
                                     views[view_type] = \
-                                        relation_obj.fields_view_get(
+                                        Relation.fields_view_get(
                                             view_type=view_type)
                                     break
                         element.attrib['mode'] = ','.join(mode)
@@ -451,12 +468,12 @@
 
         if element.tag == 'button':
             button_name = element.attrib['name']
-            if button_name in self._buttons:
-                states = self._buttons[button_name]
+            if button_name in cls._buttons:
+                states = cls._buttons[button_name]
             else:
                 states = {}
-            groups = set(user_obj.get_groups())
-            button_groups = button_obj.get_groups(self._name, button_name)
+            groups = set(User.get_groups())
+            button_groups = Button.get_groups(cls.__name__, button_name)
             if button_groups and not groups & button_groups:
                 states = states.copy()
                 states['readonly'] = True
@@ -466,14 +483,14 @@
         if Transaction().language != 'en_US':
             for attr in ('string', 'sum', 'confirm', 'help'):
                 if element.get(attr):
-                    trans = translation_obj.get_source(self._name, 'view',
+                    trans = Translation.get_source(cls.__name__, 'view',
                             Transaction().language, element.get(attr))
                     if trans:
                         element.set(attr, trans)
 
         # Set header string
         if element.tag in ('form', 'tree', 'graph'):
-            element.set('string', self.view_header_get(
+            element.set('string', cls.view_header_get(
                 element.get('string') or '', view_type=element.tag))
 
         if element.tag == 'tree' and element.get('sequence'):
@@ -481,26 +498,26 @@
 
         if childs:
             for field in element:
-                fields_attrs.update(self.__view_look_dom(field, type,
+                fields_attrs.update(cls.__view_look_dom(field, type,
                     fields_width=fields_width))
         return fields_attrs
 
     @staticmethod
     def button(func):
         @wraps(func)
-        def wrapper(self, *args, **kwargs):
+        def wrapper(cls, *args, **kwargs):
             pool = Pool()
-            button_obj = pool.get('ir.model.button')
-            user_obj = pool.get('res.user')
+            Button = pool.get('ir.model.button')
+            User = pool.get('res.user')
 
             if Transaction().user != 0:
-                groups = set(user_obj.get_groups())
-                button_groups = button_obj.get_groups(self._name,
+                groups = set(User.get_groups())
+                button_groups = Button.get_groups(cls.__name__,
                     func.__name__)
                 if button_groups and not groups & button_groups:
                     raise UserError('Calling button %s on %s is not allowed!'
-                        % (func.__name__, self._name))
-            return func(self, *args, **kwargs)
+                        % (func.__name__, cls.__name__))
+            return func(cls, *args, **kwargs)
         return wrapper
 
     @staticmethod
@@ -511,14 +528,14 @@
             @wraps(func)
             def wrapper(*args, **kwargs):
                 pool = Pool()
-                model_data_obj = pool.get('ir.model.data')
-                action_obj = pool.get('ir.action')
+                ModelData = pool.get('ir.model.data')
+                Action = pool.get('ir.action')
 
                 func(*args, **kwargs)
 
                 module, fs_id = action.split('.')
-                action_id = action_obj.get_action_id(
-                    model_data_obj.get_id(module, fs_id))
+                action_id = Action.get_action_id(
+                    ModelData.get_id(module, fs_id))
                 return action_id
             return wrapper
         return decorator
diff -r fb51f5b4aa4c trytond/model/workflow.py
--- a/trytond/model/workflow.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/model/workflow.py	Mon Sep 24 20:36:36 2012 +0200
@@ -9,36 +9,35 @@
     """
     _transition_state = 'state'
 
-    def __init__(self):
-        super(Workflow, self).__init__()
-        self._transitions = set()
+    @classmethod
+    def __setup__(cls):
+        super(Workflow, cls).__setup__()
+        cls._transitions = set()
 
     @staticmethod
     def transition(state):
         def check_transition(func):
             @wraps(func)
-            def wrapper(self, ids, *args, **kwargs):
-                records = self.browse(ids)
+            def wrapper(cls, records, *args, **kwargs):
                 filtered = []
                 to_update = {}
 
                 for record in records:
-                    current_state = getattr(record, self._transition_state)
+                    current_state = getattr(record, cls._transition_state)
                     transition = (current_state, state)
-                    if transition in self._transitions:
-                        filtered.append(record.id)
+                    if transition in cls._transitions:
+                        filtered.append(record)
                         if current_state != state:
-                            to_update[record.id] = current_state
+                            to_update[record] = current_state
 
-                result = func(self, filtered, *args, **kwargs)
+                result = func(cls, filtered, *args, **kwargs)
                 if to_update:
-                    records = self.browse(to_update.keys())
-                    for record in records:
-                        current_state = getattr(record, self._transition_state)
-                        if current_state != to_update[record.id]:
-                            del to_update[record.id]
-                    self.write(to_update.keys(), {
-                            self._transition_state: state,
+                    for record in to_update.keys():
+                        current_state = getattr(record, cls._transition_state)
+                        if current_state != to_update[record]:
+                            del to_update[record]
+                    cls.write(to_update.keys(), {
+                            cls._transition_state: state,
                             })
                 return result
             return wrapper
diff -r fb51f5b4aa4c trytond/modules/__init__.py
--- a/trytond/modules/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/modules/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -215,7 +215,7 @@
             continue
         logger.info(module)
         sys.stdout.flush()
-        objects = pool.instanciate(module)
+        classes = pool.setup(module)
         package_state = module2state.get(module, 'uninstalled')
         if (is_module_to_install(module)
                 or package_state in ('to install', 'to upgrade')):
@@ -223,13 +223,13 @@
                 package_state = 'to install'
             for child in package.childs:
                 module2state[child.name] = package_state
-            for type in objects.keys():
-                for obj in objects[type]:
-                    logger.info('%s:init %s' % (module, obj._name))
-                    obj.init(module)
-            for model in objects['model']:
+            for type in classes.keys():
+                for cls in classes[type]:
+                    logger.info('%s:register %s' % (module, cls.__name__))
+                    cls.__register__(module)
+            for model in classes['model']:
                 if hasattr(model, '_history'):
-                    models_to_update_history.add(model._name)
+                    models_to_update_history.add(model.__name__)
 
             #Instanciate a new parser for the package:
             tryton_parser = convert.TrytondXmlHandler(pool=pool, module=module)
@@ -251,8 +251,8 @@
                     continue
                 logger.info('%s:loading %s' % (module,
                         filename[len(package.info['directory']) + 1:]))
-                translation_obj = pool.get('ir.translation')
-                translation_obj.translation_import(lang2, module, filename)
+                Translation = pool.get('ir.translation')
+                Translation.translation_import(lang2, module, filename)
 
             cursor.execute('SELECT id FROM ir_module_module '
                 'WHERE name = %s', (package.name,))
@@ -271,23 +271,23 @@
 
     # Create missing reports
     from trytond.report import Report
-    report_obj = pool.get('ir.action.report')
-    report_ids = report_obj.search([
-        ('module', '=', module),
-        ])
+    ActionReport = pool.get('ir.action.report')
+    reports = ActionReport.search([
+            ('module', '=', module),
+            ])
     report_names = pool.object_name_list(type='report')
-    for report in report_obj.browse(report_ids):
+    for report in reports:
         report_name = report.report_name
         if report_name not in report_names:
             report = object.__new__(Report)
-            report._name = report_name
+            report.__name__ = report_name
             pool.add(report, type='report')
             report.__init__()
 
     for model_name in models_to_update_history:
         model = pool.get(model_name)
         if model._history:
-            logger.info('history:update %s' % model._name)
+            logger.info('history:update %s' % model.__name__)
             model._update_history_table()
 
     # Vacuum :
@@ -320,9 +320,13 @@
     Import modules to register the classes in the Pool
     '''
     import trytond.ir
+    trytond.ir.register()
     import trytond.res
+    trytond.res.register()
     import trytond.webdav
+    trytond.webdav.register()
     import trytond.test
+    trytond.test.register()
     logger = logging.getLogger('modules')
 
     for package in create_graph(get_module_list())[0]:
@@ -338,7 +342,10 @@
                     [MODULES_PATH])
             try:
                 imp.load_module('trytond.modules.' + module, mod_file,
-                        pathname, description)
+                        pathname, description).register()
+            except AttributeError:
+                # Some modules register nothing in the Pool
+                pass
             finally:
                 if mod_file is not None:
                     mod_file.close()
@@ -361,7 +368,7 @@
                     [mod_path])
             try:
                 imp.load_module('trytond.modules.' + module, mod_file,
-                        pathname, description)
+                        pathname, description).register()
             finally:
                 if mod_file is not None:
                     mod_file.close()
@@ -428,8 +435,8 @@
                 cursor.commit()
                 res = False
 
-            module_obj = pool.get('ir.module.module')
-            module_obj.update_list()
+            Module = pool.get('ir.module.module')
+            Module.update_list()
         cursor.commit()
     Cache.resets(database_name)
     return res
diff -r fb51f5b4aa4c trytond/pool.py
--- a/trytond/pool.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/pool.py	Mon Sep 24 20:36:36 2012 +0200
@@ -6,6 +6,17 @@
 from trytond.transaction import Transaction
 import __builtin__
 
+__all__ = ['Pool', 'PoolMeta']
+
+
+class PoolMeta(type):
+
+    def __new__(cls, name, bases, dct):
+        new = type.__new__(cls, name, bases, dct)
+        if '__name__' in dct:
+            new.__name__ = dct['__name__']
+        return new
+
 
 class Pool(object):
 
@@ -41,19 +52,17 @@
         self.database_name = database_name
 
     @staticmethod
-    def register(klass, type='model'):
+    def register(*classes, **kwargs):
         '''
-        Register a class
-
-        :param klass: the class
-        :param type: the type
+        Register a list of classes
         '''
-        module = None
-        for module in klass.__module__.split('.'):
-            if module != 'trytond' and module != 'modules':
-                break
-        if module:
-            Pool.classes[type].setdefault(module, []).append(klass)
+        module = kwargs['module']
+        type_ = kwargs['type_']
+        assert type_ in ('model', 'report', 'wizard')
+        for cls in classes:
+            mpool = Pool.classes[type_].setdefault(module, [])
+            assert cls not in mpool, cls
+            mpool.append(cls)
 
     @classmethod
     def start(cls):
@@ -61,6 +70,8 @@
         Start/restart the Pool
         '''
         with cls._lock:
+            for classes in Pool.classes.itervalues():
+                classes.clear()
             register_classes()
             cls._started = True
 
@@ -140,22 +151,17 @@
                 from trytond.report import Report
                 # Keyword argument 'type' conflicts with builtin function
                 cls = __builtin__.type(str(name), (Report,), {})
-                obj = object.__new__(cls)
-                obj._name = name
-                self.add(obj, type)
-                obj.__init__()
-                return obj
+                cls.__setup__()
+                self.add(cls, type)
+                return cls
             raise
 
-    def add(self, obj, type='model'):
+    def add(self, cls, type='model'):
         '''
-        Add an object to the pool
-
-        :param obj: the object
-        :param type: the type
+        Add a classe to the pool
         '''
         with self._locks[self.database_name]:
-            self._pool[self.database_name][type][obj._name] = obj
+            self._pool[self.database_name][type][cls.__name__] = cls
 
     def object_name_list(self, type='model'):
         '''
@@ -180,23 +186,29 @@
         '''
         return self._pool[self.database_name][type].iteritems()
 
-    def instanciate(self, module):
+    def setup(self, module):
         '''
-        Instanciate objects for a module
-
-        :param: the module name
-        :return: a dictionary with each type as key
-            and a list of object as value
+        Setup classes for module and return a list of classes for each type in
+        a dictionary.
         '''
-        res = {}
-        for _type in self.classes.keys():
-            res[_type] = []
-            for cls in self.classes[_type].get(module, []):
-                if cls._name in self._pool[self.database_name][_type].keys():
-                    parent_cls = self.get(cls._name, type=_type).__class__
-                    cls = type(cls._name, (cls, parent_cls), {})
-                obj = object.__new__(cls)
-                self.add(obj, type=_type)
-                obj.__init__()
-                res[_type].append(obj)
-        return res
+        classes = {}
+        for type_ in self.classes.keys():
+            classes[type_] = []
+            for cls in self.classes[type_].get(module, []):
+                try:
+                    previous_cls = self.get(cls.__name__, type=type_)
+                    cls = type(cls.__name__, (cls, previous_cls), {})
+                except KeyError:
+                    pass
+                try:
+                    cls.__setup__()
+                except AttributeError:
+                    if issubclass(cls.__class__, PoolMeta):
+                        continue
+                    raise
+                self.add(cls, type=type_)
+                classes[type_].append(cls)
+            for cls in classes[type_]:
+                if hasattr(cls, '__post_setup__'):
+                    cls.__post_setup__()
+        return classes
diff -r fb51f5b4aa4c trytond/protocols/dispatcher.py
--- a/trytond/protocols/dispatcher.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/protocols/dispatcher.py	Mon Sep 24 20:36:36 2012 +0200
@@ -20,10 +20,11 @@
 from trytond.cache import Cache
 from trytond.exceptions import UserError, UserWarning, NotLogged, \
     ConcurrencyException
+from trytond.rpc import RPC
 
 
 def dispatch(host, port, protocol, database_name, user, session, object_type,
-        object_name, method, *args, **kargs):
+        object_name, method, *args, **kwargs):
 
     if object_type == 'common':
         if method == 'login':
@@ -68,7 +69,7 @@
             ]
         elif method == 'db_exist':
             try:
-                database = Database(*args, **kargs).connect()
+                database = Database(*args, **kwargs).connect()
                 cursor = database.cursor()
                 cursor.close(close=True)
                 return True
@@ -88,13 +89,13 @@
                 res = []
             return res
         elif method == 'create':
-            return create(*args, **kargs)
+            return create(*args, **kwargs)
         elif method == 'restore':
-            return restore(*args, **kargs)
+            return restore(*args, **kwargs)
         elif method == 'drop':
-            return drop(*args, **kargs)
+            return drop(*args, **kwargs)
         elif method == 'dump':
-            return dump(*args, **kargs)
+            return dump(*args, **kwargs)
         return
     elif object_type == 'system':
         database = Database(database_name).connect()
@@ -131,34 +132,36 @@
             pool.init()
     obj = pool.get(object_name, type=object_type)
 
-    if method in obj._rpc:
-        readonly = not obj._rpc[method]
+    if method in obj.__rpc__:
+        rpc = obj.__rpc__[method]
     elif method in getattr(obj, '_buttons', {}):
-        readonly = False
+        rpc = RPC(readonly=False, instantiate=0)
     else:
         raise UserError('Calling method %s on %s %s is not allowed!'
             % (method, object_type, object_name))
 
     for count in range(int(CONFIG['retry']), -1, -1):
         with Transaction().start(database_name, user,
-                readonly=readonly) as transaction:
+                readonly=rpc.readonly) as transaction:
             try:
-
-                args_without_context = list(args)
-                if 'context' in kargs:
-                    context = kargs.pop('context')
+                args, kwargs, transaction.context, transaction.timestamp = \
+                    rpc.convert(obj, *args, **kwargs)
+                meth = getattr(obj, method)
+                if not hasattr(meth, 'im_self') or meth.im_self:
+                    result = rpc.result(meth(*args, **kwargs))
                 else:
-                    context = args_without_context.pop()
-                if '_timestamp' in context:
-                    transaction.timestamp = context['_timestamp']
-                    del context['_timestamp']
-                transaction.context = context
-                res = getattr(obj, method)(*args_without_context, **kargs)
-                if not readonly:
+                    assert rpc.instantiate == 0
+                    inst = args.pop(0)
+                    if hasattr(inst, method):
+                        result = rpc.result(meth(inst, *args, **kwargs))
+                    else:
+                        result = [rpc.result(meth(i, *args, **kwargs))
+                            for i in inst]
+                if not rpc.readonly:
                     transaction.cursor.commit()
             except DatabaseOperationalError, exception:
                 transaction.cursor.rollback()
-                if count and not readonly:
+                if count and not rpc.readonly:
                     continue
                 raise
             except Exception, exception:
@@ -176,16 +179,16 @@
         if not (object_name == 'res.request' and method == 'request_get'):
             with Transaction().start(database_name, 0) as transaction:
                 pool = Pool(database_name)
-                session_obj = pool.get('ir.session')
+                Session = pool.get('ir.session')
                 try:
-                    session_obj.reset(session)
+                    Session.reset(session)
                 except DatabaseOperationalError:
                     # Silently fail when reseting session
                     transaction.cursor.rollback()
                 else:
                     transaction.cursor.commit()
         Cache.resets(database_name)
-        return res
+        return result
 
 
 def create(database_name, password, lang, admin_password):
@@ -237,9 +240,9 @@
             cursor.execute('UPDATE res_user ' \
                     'SET password = %s ' \
                     'WHERE login = \'admin\'', (admin_password,))
-            module_obj = pool.get('ir.module.module')
-            if module_obj:
-                module_obj.update_list()
+            Module = pool.get('ir.module.module')
+            if Module:
+                Module.update_list()
             cursor.commit()
             res = True
     except Exception:
diff -r fb51f5b4aa4c trytond/protocols/webdav.py
--- a/trytond/protocols/webdav.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/protocols/webdav.py	Mon Sep 24 20:36:36 2012 +0200
@@ -174,12 +174,12 @@
             return res
         pool = Pool(Transaction().cursor.database_name)
         try:
-            collection_obj = pool.get('webdav.collection')
+            Collection = pool.get('webdav.collection')
             scheme, netloc, path, params, query, fragment = \
                 urlparse.urlparse(uri)
             if path[-1:] != '/':
                 path += '/'
-            for child in collection_obj.get_childs(dburi, filter=filter,
+            for child in Collection.get_childs(dburi, filter=filter,
                     cache=CACHE):
                 res.append(urlparse.urlunparse((scheme, netloc,
                             path + child.encode('utf-8'), params, query,
@@ -230,9 +230,9 @@
             res += '</html>'
             return res
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_data(dburi, cache=CACHE)
+            res = Collection.get_data(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -261,9 +261,9 @@
         if not dbname or not dburi:
             raise DAV_Forbidden
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.put(dburi, data, content_type, cache=CACHE)
+            res = Collection.put(dburi, data, content_type, cache=CACHE)
             Transaction().cursor.commit()
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
@@ -284,9 +284,9 @@
         if not dbname or not dburi:
             raise DAV_Forbidden
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.mkcol(dburi, cache=CACHE)
+            res = Collection.mkcol(dburi, cache=CACHE)
             Transaction().cursor.commit()
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
@@ -303,9 +303,9 @@
         if not dbname or not dburi:
             return COLLECTION
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_resourcetype(dburi, cache=CACHE)
+            res = Collection.get_resourcetype(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -320,8 +320,8 @@
             return uri.split('/')[-1]
         pool = Pool(Transaction().cursor.database_name)
         try:
-            collection_obj = pool.get('webdav.collection')
-            res = collection_obj.get_displayname(dburi, cache=CACHE)
+            Collection = pool.get('webdav.collection')
+            res = Collection.get_displayname(dburi, cache=CACHE)
         except KeyError:
             raise DAV_NotFound
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
@@ -337,9 +337,9 @@
         if not dbname or not dburi:
             return '0'
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_contentlength(dburi, cache=CACHE)
+            res = Collection.get_contentlength(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -353,9 +353,9 @@
         if not dbname or self.is_collection(uri):
             return "text/html"
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_contenttype(dburi, cache=CACHE)
+            res = Collection.get_contenttype(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -372,9 +372,9 @@
         if not dbname or not dburi:
             return time.time()
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_creationdate(dburi, cache=CACHE)
+            res = Collection.get_creationdate(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -388,9 +388,9 @@
         if not dbname or not dburi:
             return time.time()
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_lastmodified(dburi, cache=CACHE)
+            res = Collection.get_lastmodified(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -404,9 +404,9 @@
         if not dbname or not dburi:
             return 403
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.rmcol(dburi, cache=CACHE)
+            res = Collection.rmcol(dburi, cache=CACHE)
             Transaction().cursor.commit()
         except Exception, exception:
             self._log_exception(exception)
@@ -419,9 +419,9 @@
         if not dbname or not dburi:
             return 403
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.rm(dburi, cache=CACHE)
+            res = Collection.rm(dburi, cache=CACHE)
             Transaction().cursor.commit()
         except Exception, exception:
             self._log_exception(exception)
@@ -434,9 +434,9 @@
         if not dbname or not dburi:
             return 1
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.exists(dburi, cache=CACHE)
+            res = Collection.exists(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -485,8 +485,8 @@
         else:
             pool = Pool(Transaction().cursor.database_name)
             try:
-                collection_obj = pool.get('webdav.collection')
-                privileges = collection_obj.current_user_privilege_set(dburi,
+                Collection = pool.get('webdav.collection')
+                privileges = Collection.current_user_privilege_set(dburi,
                         cache=CACHE)
             except KeyError:
                 pass
@@ -579,8 +579,8 @@
                     pool = Pool(dbname)
                     if not dbname in database_list:
                         pool.init()
-                    share_obj = pool.get('webdav.share')
-                    user = share_obj.get_login(key, command, path)
+                    Share = pool.get('webdav.share')
+                    user = Share.get_login(key, command, path)
                     transaction.cursor.commit()
             if not user:
                 return None
diff -r fb51f5b4aa4c trytond/pyson.py
--- a/trytond/pyson.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/pyson.py	Mon Sep 24 20:36:36 2012 +0200
@@ -547,8 +547,8 @@
 
     def pyson(self):
         from trytond.pool import Pool
-        model_data_obj = Pool().get('ir.model.data')
-        return model_data_obj.get_id(self._module, self._fs_id)
+        ModelData = Pool().get('ir.model.data')
+        return ModelData.get_id(self._module, self._fs_id)
 
     def types(self):
         return set([int])
diff -r fb51f5b4aa4c trytond/report/report.py
--- a/trytond/report/report.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/report/report.py	Mon Sep 24 20:36:36 2012 +0200
@@ -22,9 +22,10 @@
 from genshi.filters import Translator
 import lxml.etree
 from trytond.config import CONFIG
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 from trytond.url import URLMixin
+from trytond.rpc import RPC
 
 MIMETYPES = {
     'odt': 'application/vnd.oasis.opendocument.text',
@@ -55,9 +56,10 @@
 
 class ReportFactory:
 
-    def __call__(self, objects, **kwargs):
+    def __call__(self, records, **kwargs):
         data = {}
-        data['objects'] = objects
+        data['objects'] = records  # XXX To remove
+        data['records'] = records
         data.update(kwargs)
         return data
 
@@ -73,7 +75,7 @@
     def __call__(self, text):
         if self.language not in self.cache:
             self.cache[self.language] = {}
-            translation_ids = self.translation.search([
+            translations = self.translation.search([
                 ('lang', '=', self.language),
                 ('type', '=', 'odt'),
                 ('name', '=', self.report_name),
@@ -82,7 +84,7 @@
                 ('fuzzy', '=', False),
                 ('res_id', '=', None),
                 ])
-            for translation in self.translation.browse(translation_ids):
+            for translation in translations:
                 self.cache[self.language][translation.src] = translation.value
         return self.cache[self.language].get(text, text)
 
@@ -91,78 +93,92 @@
 
 
 class Report(URLMixin):
-    _name = ""
+    __metaclass__ = PoolMeta
 
-    def __new__(cls):
-        Pool.register(cls, type='report')
+    @classmethod
+    def __setup__(cls):
+        cls.__rpc__ = {
+            'execute': RPC(),
+            }
 
-    def __init__(self):
-        self._rpc = {
-            'execute': False,
-        }
-
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         pass
 
-    def execute(self, ids, datas):
+    @classmethod
+    def execute(cls, ids, data):
         '''
-        Execute the report.
-
-        :param ids: a list of record ids on which execute report
-        :param datas: a dictionary with datas that will be set in
-            local context of the report
-        :return: a tuple with:
+        Execute the report on record ids.
+        The dictionary with data that will be set in local context of the
+        report.
+        It returns a tuple with:
             report type,
             data,
             a boolean to direct print,
             the report name
         '''
         pool = Pool()
-        action_report_obj = pool.get('ir.action.report')
-        action_report_ids = action_report_obj.search([
-            ('report_name', '=', self._name)
-            ])
-        if not action_report_ids:
-            raise Exception('Error', 'Report (%s) not find!' % self._name)
-        action_report = action_report_obj.browse(action_report_ids[0])
-        objects = None
+        ActionReport = pool.get('ir.action.report')
+        action_reports = ActionReport.search([
+                ('report_name', '=', cls.__name__)
+                ])
+        if not action_reports:
+            raise Exception('Error', 'Report (%s) not find!' % cls.__name__)
+        action_report = action_reports[0]
+        records = None
         if action_report.model:
-            objects = self._get_objects(ids, action_report.model, datas)
-        type, data = self.parse(action_report, objects, datas, {})
+            records = cls._get_records(ids, action_report.model, data)
+        type, data = cls.parse(action_report, records, data, {})
         return (type, buffer(data), action_report.direct_print,
-                action_report.name)
+            action_report.name)
 
-    def _get_objects(self, ids, model, datas):
+    @classmethod
+    def _get_records(cls, ids, model, data):
         pool = Pool()
-        model_obj = pool.get(model)
-        return model_obj.browse(ids)
+        Model = pool.get(model)
 
-    def parse(self, report, objects, datas, localcontext):
+        class TranslateModel:
+            _languages = {}
+
+            def __init__(self, id):
+                self.id = id
+                self._language = Transaction().language
+
+            def setLang(self, language):
+                self._language = language
+
+            def __getattr__(self, name):
+                if self._language not in TranslateModel._languages:
+                    with Transaction().set_context(language=self._language):
+                        records = Model.browse(ids)
+                    id2record = dict((r.id, r) for r in records)
+                    TranslateModel._languages[self._language] = id2record
+                else:
+                    id2record = TranslateModel._languages[self._language]
+                record = id2record[self.id]
+                return getattr(record, name)
+        return [TranslateModel(id) for id in ids]
+
+    @classmethod
+    def parse(cls, report, records, data, localcontext):
         '''
-        Parse the report.
-
-        :param report: a BrowseRecord of the ir.action.report
-        :param objects: a BrowseRecordList of the records on which parse report
-        :param datas: a dictionary with datas that will be set in local context
-            of the report
-        :param localcontext: the context used to parse the report
-        :return: a tuple with:
-            report type
-            report
+        Parse the report and return a tuple with report type and report.
         '''
         pool = Pool()
-        localcontext['datas'] = datas
-        localcontext['user'] = pool.get('res.user'
-                ).browse(Transaction().user)
+        User = pool.get('res.user')
+        Translation = pool.get('ir.translation')
+
+        localcontext['data'] = data
+        localcontext['user'] = User(Transaction().user)
         localcontext['formatLang'] = lambda *args, **kargs: \
-                self.format_lang(*args, **kargs)
+            cls.format_lang(*args, **kargs)
         localcontext['StringIO'] = StringIO.StringIO
         localcontext['time'] = time
         localcontext['datetime'] = datetime
         localcontext['context'] = Transaction().context
 
-        translate = TranslateFactory(self._name, Transaction().language,
-                pool.get('ir.translation'))
+        translate = TranslateFactory(cls.__name__, Transaction().language,
+            Translation)
         localcontext['setLang'] = lambda language: translate.set_language(
             language)
 
@@ -257,9 +273,10 @@
             localcontext.iteritems()))
         #Test compatibility with old relatorio version <= 0.3.0
         if len(inspect.getargspec(rel_report.__call__)[0]) == 2:
-            data = rel_report(objects, **localcontext).render().getvalue()
+            data = rel_report(records, **localcontext).render().getvalue()
         else:
-            localcontext['objects'] = objects
+            localcontext['objects'] = records  # XXX to remove
+            localcontext['records'] = records
             data = rel_report(**localcontext).render()
             if hasattr(data, 'getvalue'):
                 data = data.getvalue()
@@ -267,11 +284,12 @@
         os.remove(path)
         output_format = report.extension or report.template_extension
         if output_format not in MIMETYPES:
-            data = self.unoconv(data, report.template_extension, output_format)
+            data = cls.unoconv(data, report.template_extension, output_format)
         oext = FORMAT2EXT.get(output_format, output_format)
         return (oext, data)
 
-    def unoconv(self, data, input_format, output_format):
+    @classmethod
+    def unoconv(cls, data, input_format, output_format):
         '''
         Call unoconv to convert the OpenDocument
         '''
@@ -291,17 +309,18 @@
         finally:
             os.remove(path)
 
-    def format_lang(self, value, lang, digits=2, grouping=True, monetary=False,
+    @classmethod
+    def format_lang(cls, value, lang, digits=2, grouping=True, monetary=False,
             date=False, currency=None, symbol=True):
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
+        Lang = pool.get('ir.lang')
 
         if date:
             if lang:
                 locale_format = lang.date
                 code = lang.code
             else:
-                locale_format = lang_obj.default_date()
+                locale_format = Lang.default_date()
                 code = CONFIG['language']
             if not isinstance(value, time.struct_time):
                 # assume string, parse it
@@ -318,9 +337,9 @@
                     string_pattern)[:6])
             else:
                 date = datetime.datetime(*(value.timetuple()[:6]))
-            return lang_obj.strftime(date, code, locale_format)
+            return Lang.strftime(date, code, locale_format)
         if currency:
-            return lang_obj.currency(lang, value, currency, grouping=grouping,
-                    symbol=symbol)
-        return lang_obj.format(lang, '%.' + str(digits) + 'f', value,
-                grouping=grouping, monetary=monetary)
+            return Lang.currency(lang, value, currency, grouping=grouping,
+                symbol=symbol)
+        return Lang.format(lang, '%.' + str(digits) + 'f', value,
+            grouping=grouping, monetary=monetary)
diff -r fb51f5b4aa4c trytond/res/__init__.py
--- a/trytond/res/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/res/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,6 +1,38 @@
 #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 ..pool import Pool
 from .group import *
 from .user import *
 from .request import *
 from .ir import *
+
+
+def register():
+    Pool.register(
+        Group,
+        User,
+        Group2,
+        UserAction,
+        UserGroup,
+        Warning_,
+        UserConfigStart,
+        Request,
+        RequestLink,
+        RequestHistory,
+        RequestReference,
+        UIMenuGroup,
+        ActionGroup,
+        ModelFieldGroup,
+        ModelButtonGroup,
+        RuleGroupGroup,
+        RuleGroupUser,
+        Lang,
+        SequenceType,
+        SequenceTypeGroup,
+        Sequence,
+        SequenceStrict,
+        ModuleConfigWizardItem,
+        module='res', type_='model')
+    Pool.register(
+        UserConfig,
+        module="res", type_='wizard')
diff -r fb51f5b4aa4c trytond/res/group.py
--- a/trytond/res/group.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/res/group.py	Mon Sep 24 20:36:36 2012 +0200
@@ -4,22 +4,26 @@
 from itertools import chain
 from ..model import ModelView, ModelSQL, fields
 from ..transaction import Transaction
-from ..pool import Pool
+from ..pool import Pool, PoolMeta
+
+__all__ = [
+    'Group', 'Group2',
+    ]
 
 
 class MenuMany2Many(fields.Many2Many):
 
     def get(self, ids, model, name, values=None):
-        menu_obj = self.get_target()
+        Menu = self.get_target()
         res = super(MenuMany2Many, self).get(ids, model, name,
                 values=values)
         menu_ids = list(set(chain(*res.values())))
         test_ids = []
         for i in range(0, len(menu_ids), Transaction().cursor.IN_MAX):
             sub_ids = menu_ids[i:i + Transaction().cursor.IN_MAX]
-            test_ids.append(menu_obj.search([
-                ('id', 'in', sub_ids),
-                ]))
+            test_ids.append(map(int, Menu.search([
+                            ('id', 'in', sub_ids),
+                            ])))
         menu_ids = set(chain(*test_ids))
         for ids in res.itervalues():
             for id_ in ids[:]:
@@ -30,8 +34,7 @@
 
 class Group(ModelSQL, ModelView):
     "Group"
-    _name = "res.group"
-    _description = __doc__
+    __name__ = "res.group"
     name = fields.Char('Name', required=True, select=True, translate=True)
     model_access = fields.One2Many('ir.model.access', 'group',
        'Access Model')
@@ -43,67 +46,68 @@
     menu_access = MenuMany2Many('ir.ui.menu-res.group',
        'group', 'menu', 'Access Menu')
 
-    def __init__(self):
-        super(Group, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Group, cls).__setup__()
+        cls._sql_constraints += [
             ('name_uniq', 'unique (name)',
                 'The name of the group must be unique!')
         ]
 
-    def copy(self, ids, default=None):
-        int_id = isinstance(ids, (int, long))
-        if int_id:
-            ids = [ids]
-
+    @classmethod
+    def copy(cls, groups, default=None):
         if default is None:
             default = {}
         default = default.copy()
 
-        new_ids = []
-        for group in self.browse(ids):
+        new_groups = []
+        for group in groups:
             i = 1
             while True:
                 name = '%s (%d)' % (group.name, i)
-                if not self.search([('name', '=', name)], order=[]):
+                if not cls.search([('name', '=', name)], order=[]):
                     break
                 i += 1
             default['name'] = name
-            new_ids.append(super(Group, self).copy(group.id, default=default))
-        if int_id:
-            return new_ids[0]
-        return new_ids
+            new_groups.extend(super(Group, cls).copy([group], default=default))
+        return new_groups
 
-    def create(self, vals):
-        res = super(Group, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        res = super(Group, cls).create(vals)
         pool = Pool()
         # Restart the cache on the domain_get method
-        pool.get('ir.rule').domain_get.reset()
+        pool.get('ir.rule')._domain_get_cache.clear()
         # Restart the cache for get_groups
-        pool.get('res.user').get_groups.reset()
+        pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
-        pool.get('res.user').get_preferences.reset()
+        pool.get('res.user')._get_preferences_cache.clear()
         return res
 
-    def write(self, ids, vals):
-        res = super(Group, self).write(ids, vals)
+    @classmethod
+    def write(cls, groups, vals):
+        super(Group, cls).write(groups, vals)
         pool = Pool()
         # Restart the cache on the domain_get method
-        pool.get('ir.rule').domain_get.reset()
+        pool.get('ir.rule')._domain_get_cache.clear()
         # Restart the cache for get_groups
-        pool.get('res.user').get_groups.reset()
+        pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
-        pool.get('res.user').get_preferences.reset()
-        return res
+        pool.get('res.user')._get_preferences_cache.clear()
 
-    def delete(self, ids):
-        res = super(Group, self).delete(ids)
+    @classmethod
+    def delete(cls, groups):
+        super(Group, cls).delete(groups)
         pool = Pool()
         # Restart the cache on the domain_get method
-        pool.get('ir.rule').domain_get.reset()
+        pool.get('ir.rule')._domain_get_cache.clear()
         # Restart the cache for get_groups
-        pool.get('res.user').get_groups.reset()
+        pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
-        pool.get('res.user').get_preferences.reset()
-        return res
+        pool.get('res.user')._get_preferences_cache.clear()
 
-Group()
+
+class Group2:
+    __metaclass__ = PoolMeta
+    __name__ = "res.group"
+    users = fields.Many2Many('res.user-res.group', 'group', 'user', 'Users')
diff -r fb51f5b4aa4c trytond/res/ir.py
--- a/trytond/res/ir.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/res/ir.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,338 +1,320 @@
 #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 ..model import ModelView, ModelSQL, Model, fields
+from ..model import ModelSQL, fields
 from ..backend import TableHandler
 from ..transaction import Transaction
-from ..pool import Pool
+from ..pool import Pool, PoolMeta
+
+__all__ = [
+    'UIMenuGroup', 'ActionGroup', 'ModelFieldGroup', 'ModelButtonGroup',
+    'RuleGroupGroup', 'RuleGroupUser', 'Lang', 'SequenceType',
+    'SequenceTypeGroup', 'Sequence', 'SequenceStrict',
+    'ModuleConfigWizardItem',
+    ]
+__metaclass__ = PoolMeta
 
 
 class UIMenuGroup(ModelSQL):
     "UI Menu - Group"
-    _name = 'ir.ui.menu-res.group'
-    _description = __doc__
+    __name__ = 'ir.ui.menu-res.group'
     menu = fields.Many2One('ir.ui.menu', 'Menu', ondelete='CASCADE',
             select=True, required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'ir_ui_menu_group_rel', self._table)
+        TableHandler.table_rename(cursor, 'ir_ui_menu_group_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'ir_ui_menu_group_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 menu_id and gid renamed into menu group
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('menu_id', 'menu')
         table.column_rename('gid', 'group')
-        super(UIMenuGroup, self).init(module_name)
+        super(UIMenuGroup, cls).__register__(module_name)
 
-    def create(self, vals):
-        res = super(UIMenuGroup, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        res = super(UIMenuGroup, cls).create(vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
+        Pool().get('ir.rule')._domain_get_cache.clear()
         return res
 
-    def write(self, ids, vals):
-        res = super(UIMenuGroup, self).write(ids, vals)
+    @classmethod
+    def write(cls, records, vals):
+        super(UIMenuGroup, cls).write(records, vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
-    def delete(self, ids):
-        res = super(UIMenuGroup, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        super(UIMenuGroup, cls).delete(records)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-UIMenuGroup()
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
 
 class ActionGroup(ModelSQL):
     "Action - Group"
-    _name = 'ir.action-res.group'
-    _description = __doc__
+    __name__ = 'ir.action-res.group'
     action = fields.Many2One('ir.action', 'Action', ondelete='CASCADE',
             select=True, required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'ir_action_group_rel', self._table)
+        TableHandler.table_rename(cursor, 'ir_action_group_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'ir_action_group_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 action_id and gid renamed into action and group
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('action_id', 'action')
         table.column_rename('gid', 'group')
-        super(ActionGroup, self).init(module_name)
+        super(ActionGroup, cls).__register__(module_name)
 
-    def create(self, vals):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def create(cls, vals):
+        Action = Pool().get('ir.action')
         if vals.get('action'):
             vals = vals.copy()
-            vals['action'] = action_obj.get_action_id(vals['action'])
-        res = super(ActionGroup, self).create(vals)
+            vals['action'] = Action.get_action_id(vals['action'])
+        res = super(ActionGroup, cls).create(vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
+        Pool().get('ir.rule')._domain_get_cache.clear()
         return res
 
-    def write(self, ids, vals):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def write(cls, records, vals):
+        Action = Pool().get('ir.action')
         if vals.get('action'):
             vals = vals.copy()
-            vals['action'] = action_obj.get_action_id(vals['action'])
-        res = super(ActionGroup, self).write(ids, vals)
+            vals['action'] = Action.get_action_id(vals['action'])
+        super(ActionGroup, cls).write(records, vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
-    def delete(self, ids):
-        res = super(ActionGroup, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        super(ActionGroup, cls).delete(records)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-ActionGroup()
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
 
 class ModelFieldGroup(ModelSQL):
     "Model Field Group Rel"
-    _name = 'ir.model.field-res.group'
-    _description = __doc__
+    __name__ = 'ir.model.field-res.group'
     field_id = fields.Many2One('ir.model.field', 'Model Field',
             ondelete='CASCADE', select=True, required=True)
     group_id = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
         TableHandler.table_rename(cursor, 'ir_model_field_group_rel',
-            self._table)
+            cls._table)
         TableHandler.sequence_rename(cursor, 'ir_model_field_group_rel_id_seq',
-            self._table + '_id_seq')
-        super(ModelFieldGroup, self).init(module_name)
-
-ModelFieldGroup()
+            cls._table + '_id_seq')
+        super(ModelFieldGroup, cls).__register__(module_name)
 
 
 class ModelButtonGroup(ModelSQL):
     "Model Button - Group"
-    _name = 'ir.model.button-res.group'
-    _description = __doc__
+    __name__ = 'ir.model.button-res.group'
     button = fields.Many2One('ir.model.button', 'Button',
         ondelete='CASCADE', select=True, required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
         select=True, required=True)
     active = fields.Boolean('Active', select=True)
 
-    def __init__(self):
-        super(ModelButtonGroup, self).__init__()
-        for method in self._rpc:
-            self._rpc[method] = False
-
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def create(self, values):
+    @classmethod
+    def create(cls, values):
         pool = Pool()
-        result = super(ModelButtonGroup, self).create(values)
+        result = super(ModelButtonGroup, cls).create(values)
         # Restart the cache for get_groups
-        pool.get('ir.model.button').get_groups.reset()
+        pool.get('ir.model.button')._groups_cache.clear()
         return result
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, records, values):
         pool = Pool()
-        result = super(ModelButtonGroup, self).write(ids, values)
+        super(ModelButtonGroup, cls).write(records, values)
         # Restart the cache for get_groups
-        pool.get('ir.model.button').get_groups.reset()
-        return result
+        pool.get('ir.model.button')._groups_cache.clear()
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, records):
         pool = Pool()
-        result = super(ModelButtonGroup, self).delete(ids)
+        super(ModelButtonGroup, cls).delete(records)
         # Restart the cache for get_groups
-        pool.get('ir.model.button').get_groups.reset()
-        return result
-
-ModelButtonGroup()
+        pool.get('ir.model.button')._groups_cache.clear()
 
 
 class RuleGroupGroup(ModelSQL):
     "Rule Group - Group"
-    _name = 'ir.rule.group-res.group'
-    _description = __doc__
+    __name__ = 'ir.rule.group-res.group'
     rule_group = fields.Many2One('ir.rule.group', 'Rule Group',
             ondelete='CASCADE', select=True, required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'group_rule_group_rel', self._table)
+        TableHandler.table_rename(cursor, 'group_rule_group_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'group_rule_group_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 rule_group_id and group_id renamed into rule_group
         # and group
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('rule_group_id', 'rule_group')
         table.column_rename('group_id', 'group')
-        super(RuleGroupGroup, self).init(module_name)
-
-RuleGroupGroup()
+        super(RuleGroupGroup, cls).__register__(module_name)
 
 
 class RuleGroupUser(ModelSQL):
     "Rule Group - User"
-    _name = 'ir.rule.group-res.user'
-    _description = __doc__
+    __name__ = 'ir.rule.group-res.user'
     rule_group = fields.Many2One('ir.rule.group', 'Rule Group',
             ondelete='CASCADE', select=True, required=True)
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'user_rule_group_rel', self._table)
+        TableHandler.table_rename(cursor, 'user_rule_group_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'user_rule_group_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 rule_group_id and user_id renamed into rule_group
         # and user
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('rule_group_id', 'rule_group')
         table.column_rename('user_id', 'user')
-        super(RuleGroupUser, self).init(module_name)
+        super(RuleGroupUser, cls).__register__(module_name)
 
-RuleGroupUser()
 
+class Lang:
+    __name__ = 'ir.lang'
 
-class Lang(ModelSQL, ModelView):
-    _name = 'ir.lang'
+    @classmethod
+    def write(cls, langs, vals):
+        super(Lang, cls).write(langs, vals)
+        # Restart the cache for get_preferences
+        Pool().get('res.user')._get_preferences_cache.clear()
 
-    def write(self, ids, vals):
-        res = super(Lang, self).write(ids, vals)
-        # Restart the cache for get_preferences
-        Pool().get('res.user').get_preferences.reset()
-        return res
 
-Lang()
-
-
-class SequenceType(ModelSQL, ModelView):
-    _name = 'ir.sequence.type'
+class SequenceType:
+    __name__ = 'ir.sequence.type'
     groups = fields.Many2Many('ir.sequence.type-res.group', 'sequence_type',
             'group', 'User Groups',
             help='Groups allowed to edit the sequences of this type')
 
-SequenceType()
-
 
 class SequenceTypeGroup(ModelSQL):
     'Sequence Type - Group'
-    _name = 'ir.sequence.type-res.group'
-    _description = __doc__
+    __name__ = 'ir.sequence.type-res.group'
     sequence_type = fields.Many2One('ir.sequence.type', 'Sequence Type',
             ondelete='CASCADE', select=True, required=True)
     group = fields.Many2One('res.group', 'User Groups',
             ondelete='CASCADE', select=True, required=True)
 
-    def delete(self, ids):
-        rule_obj = Pool().get('ir.rule')
-        res = super(SequenceTypeGroup, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        Rule = Pool().get('ir.rule')
+        super(SequenceTypeGroup, cls).delete(records)
         # Restart the cache on the domain_get method of ir.rule
-        rule_obj.domain_get.reset()
+        Rule._domain_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        Rule = Pool().get('ir.rule')
+        res = super(SequenceTypeGroup, cls).create(vals)
+        # Restart the cache on the domain_get method of ir.rule
+        Rule._domain_get_cache.clear()
         return res
 
-    def create(self, vals):
-        rule_obj = Pool().get('ir.rule')
-        res = super(SequenceTypeGroup, self).create(vals)
-        # Restart the cache on the domain_get method of ir.rule
-        rule_obj.domain_get.reset()
-        return res
+    @classmethod
+    def write(cls, records, vals):
+        Rule = Pool().get('ir.rule')
+        super(SequenceTypeGroup, cls).write(records, vals)
+        # Restart the cache on the domain_get method
+        Rule._domain_get_cache.clear()
 
-    def write(self, ids, vals):
-        rule_obj = Pool().get('ir.rule')
-        res = super(SequenceTypeGroup, self).write(ids, vals)
-        # Restart the cache on the domain_get method
-        rule_obj.domain_get.reset()
-        return res
 
-SequenceTypeGroup()
-
-
-class Sequence(ModelSQL, ModelView):
-    _name = 'ir.sequence'
+class Sequence:
+    __name__ = 'ir.sequence'
     groups = fields.Function(fields.Many2Many('res.group', None, None,
         'User Groups'), 'get_groups', searcher='search_groups')
 
-    def get_groups(self, ids, name):
-        sequence_type_obj = Pool().get('ir.sequence.type')
-        sequences = self.browse(ids)
+    @classmethod
+    def get_groups(cls, sequences, name):
+        SequenceType = Pool().get('ir.sequence.type')
         code2seq = {}
         for sequence in sequences:
             code2seq.setdefault(sequence.code, []).append(sequence.id)
 
-        sequence_type_ids = sequence_type_obj.search([
+        sequence_types = SequenceType.search([
                 ('code', 'in', code2seq.keys()),
                 ])
-        sequence_types = sequence_type_obj.browse(sequence_type_ids)
 
-        res = {}
+        groups = {}
         for sequence_type in sequence_types:
             seq_ids = code2seq[sequence_type.code]
             for seq_id in seq_ids:
-                res.setdefault(seq_id, []).append(sequence_type.id)
+                groups.setdefault(seq_id, []).append(sequence_type.id)
 
-        return res
+        return groups
 
-    def search_groups(self, name, clause):
-        sequence_type_obj = Pool().get('ir.sequence.type')
-        ids = sequence_type_obj.search([clause], order=[])
-        seq_types = sequence_type_obj.browse(ids)
+    @staticmethod
+    def search_groups(name, clause):
+        SequenceType = Pool().get('ir.sequence.type')
+        seq_types = SequenceType.search([clause], order=[])
         codes = set(st.code for st in seq_types)
         return [('code', 'in', list(codes))]
 
-Sequence()
-
 
 class SequenceStrict(Sequence):
     # This empty class declaration is needed to inherit the groups
     # field
-    _name = 'ir.sequence.strict'
+    __name__ = 'ir.sequence.strict'
 
-SequenceStrict()
 
+class ModuleConfigWizardItem:
+    __name__ = 'ir.module.module.config_wizard.item'
 
-class ModuleConfigWizardItem(Model):
-    _name = 'ir.module.module.config_wizard.item'
-
-    def create(self, values):
+    @classmethod
+    def create(cls, values):
         pool = Pool()
-        user_obj = pool.get('res.user')
-        result = super(ModuleConfigWizardItem, self).create(values)
+        User = pool.get('res.user')
+        result = super(ModuleConfigWizardItem, cls).create(values)
         # Restart the cache for get_preferences
-        user_obj.get_preferences.reset()
+        User._get_preferences_cache.clear()
         return result
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, items, values):
         pool = Pool()
-        user_obj = pool.get('res.user')
-        result = super(ModuleConfigWizardItem, self).write(ids, values)
+        User = pool.get('res.user')
+        super(ModuleConfigWizardItem, cls).write(items, values)
         # Restart the cache for get_preferences
-        user_obj.get_preferences.reset()
-        return result
+        User._get_preferences_cache.clear()
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, items):
         pool = Pool()
-        user_obj = pool.get('res.user')
-        result = super(ModuleConfigWizardItem, self).delete(ids)
+        User = pool.get('res.user')
+        super(ModuleConfigWizardItem, cls).delete(items)
         # Restart the cache for get_preferences
-        user_obj.get_preferences.reset()
-        return result
-
-ModuleConfigWizardItem()
+        User._get_preferences_cache.clear()
diff -r fb51f5b4aa4c trytond/res/request.py
--- a/trytond/res/request.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/res/request.py	Mon Sep 24 20:36:36 2012 +0200
@@ -5,6 +5,11 @@
 from ..pyson import Eval, If
 from ..transaction import Transaction
 from ..pool import Pool
+from ..rpc import RPC
+
+__all__ = [
+    'Request', 'RequestLink', 'RequestHistory', 'RequestReference',
+    ]
 
 _STATES = [
     ('draft', 'Draft'),
@@ -29,8 +34,7 @@
 
 class Request(ModelSQL, ModelView):
     "Request"
-    _name = 'res.request'
-    _description = __doc__
+    __name__ = 'res.request'
     name = fields.Char('Subject', states={
             'readonly': _READONLY,
             }, required=True, depends=_DEPENDS)
@@ -59,20 +63,21 @@
                 Eval('act_from', 0) != Eval('_user', 0)),
             }, depends=['state', 'act_from'])
     number_references = fields.Function(fields.Integer('Number of References',
-        on_change_with=['references']), 'get_number_references')
+        on_change_with=['references']), 'on_change_with_number_references')
     state = fields.Selection(_STATES, 'State', required=True, readonly=True)
     history = fields.One2Many('res.request.history', 'request',
            'History', readonly=True)
 
-    def __init__(self):
-        super(Request, self).__init__()
-        self._rpc.update({
-            'request_get': False,
-        })
-        self._order.insert(0, ('priority', 'DESC'))
-        self._order.insert(1, ('trigger_date', 'DESC'))
-        self._order.insert(2, ('create_date', 'DESC'))
-        self._buttons.update({
+    @classmethod
+    def __setup__(cls):
+        super(Request, cls).__setup__()
+        cls.__rpc__.update({
+                'request_get': RPC(),
+                })
+        cls._order.insert(0, ('priority', 'DESC'))
+        cls._order.insert(1, ('trigger_date', 'DESC'))
+        cls._order.insert(2, ('create_date', 'DESC'))
+        cls._buttons.update({
                 'send': {
                     'invisible': ~Eval('state').in_(['draft', 'chatting']),
                     'readonly': Eval('act_from') != Eval('_user'),
@@ -87,37 +92,31 @@
                     },
                 })
 
-    def default_act_from(self):
+    @staticmethod
+    def default_act_from():
         return int(Transaction().user)
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_priority(self):
+    @staticmethod
+    def default_priority():
         return '1'
 
-    def on_change_with_number_references(self, vals):
-        if vals.get('references'):
-            return len(vals['references'])
-        return 0
+    def on_change_with_number_references(self, name=None):
+        return len(self.references or '')
 
-    def get_number_references(self, ids, name):
-        res = {}
-        for request in self.browse(ids):
-            if request.references:
-                res[request.id] = len(request.references)
-            else:
-                res[request.id] = 0
-        return res
-
+    @classmethod
     @ModelView.button
-    def send(self, ids):
+    def send(cls, requests):
         pool = Pool()
-        request_history_obj = pool.get('res.request.history')
-        for request in self.browse(ids):
+        RequestHistory = pool.get('res.request.history')
+        for request in requests:
             values = {
                 'request': request.id,
                 'act_from': request.act_from.id,
@@ -132,17 +131,18 @@
                 values['name'] = values['body'][:125] + '...'
             else:
                 values['name'] = values['body'] or '/'
-            request_history_obj.create(values)
-        self.write(ids, {
+            RequestHistory.create(values)
+        cls.write(requests, {
             'state': 'waiting',
             'date_sent': datetime.datetime.now(),
             })
 
+    @classmethod
     @ModelView.button
-    def reply(self, ids):
+    def reply(cls, requests):
         user = Transaction().user
-        for request in self.browse(ids):
-            self.write(request.id, {
+        for request in requests:
+            cls.write([request], {
                 'state': 'chatting',
                 'act_from': user,
                 'act_to': request.act_from.id,
@@ -150,13 +150,15 @@
                 'body': '',
                 })
 
+    @classmethod
     @ModelView.button
-    def close(self, ids):
-        self.write(ids, {'state': 'closed', 'active': False})
+    def close(cls, requests):
+        cls.write(requests, {'state': 'closed', 'active': False})
 
-    def request_get(self):
+    @classmethod
+    def request_get(cls):
         user = Transaction().user
-        ids = self.search([
+        requests = cls.search([
             ('act_to', '=', user),
             ['OR',
                 ('trigger_date', '<=', datetime.datetime.now()),
@@ -164,48 +166,45 @@
             ],
             ('active', '=', True),
             ])
-        ids2 = self.search([
+        requests2 = cls.search([
             ('act_from', '=', user),
             ('act_to', '!=', user),
             ('state', '!=', 'draft'),
             ('active', '=', True),
             ])
-        return (ids, ids2)
-
-Request()
+        return ([r.id for r in requests], [r.id for r in requests2])
 
 
 class RequestLink(ModelSQL, ModelView):
     "Request link"
-    _name = 'res.request.link'
-    _description = __doc__
+    __name__ = 'res.request.link'
     name = fields.Char('Name', required=True, translate=True)
     model = fields.Selection('models_get', 'Model', required=True)
     priority = fields.Integer('Priority', required=True)
 
-    def __init__(self):
-        super(RequestLink, self).__init__()
-        self._order.insert(0, ('priority', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(RequestLink, cls).__setup__()
+        cls._order.insert(0, ('priority', 'ASC'))
 
-    def default_priority(self):
+    @staticmethod
+    def default_priority():
         return 5
 
-    def models_get(self):
+    @staticmethod
+    def models_get():
         pool = Pool()
-        model_obj = pool.get('ir.model')
-        model_ids = model_obj.search([])
+        Model = pool.get('ir.model')
+        models = Model.search([])
         res = []
-        for model in model_obj.browse(model_ids):
+        for model in models:
             res.append((model.model, model.name))
         return res
 
-RequestLink()
-
 
 class RequestHistory(ModelSQL, ModelView):
     "Request history"
-    _name = 'res.request.history'
-    _description = __doc__
+    __name__ = 'res.request.history'
     name = fields.Char('Summary', required=True, readonly=True)
     request = fields.Many2One('res.request', 'Request', required=True,
        ondelete='CASCADE', select=True, readonly=True)
@@ -220,32 +219,35 @@
     priority = fields.Selection(_PRIORITIES, 'Priority', required=True,
             readonly=True)
 
-    def __init__(self):
-        super(RequestHistory, self).__init__()
-        self._order.insert(0, ('date_sent', 'DESC'))
+    @classmethod
+    def __setup__(cls):
+        super(RequestHistory, cls).__setup__()
+        cls._order.insert(0, ('date_sent', 'DESC'))
 
-    def default_name(self):
+    @staticmethod
+    def default_name():
         return 'No Name'
 
-    def default_act_from(self):
+    @staticmethod
+    def default_act_from():
         return int(Transaction().user)
 
-    def default_act_to(self):
+    @staticmethod
+    def default_act_to():
         return int(Transaction().user)
 
-    def default_date_sent(self):
+    @staticmethod
+    def default_date_sent():
         return datetime.datetime.now()
 
-    def write(self, ids, vals):
+    @staticmethod
+    def write(records, vals):
         pass
 
-RequestHistory()
-
 
 class RequestReference(ModelSQL, ModelView):
     "Request Reference"
-    _name = 'res.request.reference'
-    _description = __doc__
+    __name__ = 'res.request.reference'
     _rec_name = 'reference'
 
     request = fields.Many2One('res.request', 'Request', required=True,
@@ -253,11 +255,9 @@
     reference = fields.Reference('Reference', selection='links_get',
             required=True)
 
-    def links_get(self):
+    @staticmethod
+    def links_get():
         pool = Pool()
-        request_link_obj = pool.get('res.request.link')
-        ids = request_link_obj.search([])
-        request_links = request_link_obj.browse(ids)
+        RequestLink = pool.get('res.request.link')
+        request_links = RequestLink.search([])
         return [(x.model, x.name) for x in request_links]
-
-RequestReference()
diff -r fb51f5b4aa4c trytond/res/user.py
--- a/trytond/res/user.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/res/user.py	Mon Sep 24 20:36:36 2012 +0200
@@ -16,13 +16,14 @@
 
 from ..model import ModelView, ModelSQL, fields
 from ..wizard import Wizard, StateView, Button, StateTransition
-from ..tools import safe_eval, reduce_ids
+from ..tools import safe_eval
 from ..backend import TableHandler
 from ..transaction import Transaction
 from ..cache import Cache
 from ..pool import Pool
 from ..config import CONFIG
 from ..pyson import PYSONEncoder
+from ..rpc import RPC
 
 try:
     import pytz
@@ -30,11 +31,15 @@
 except ImportError:
     TIMEZONES = []
 
+__all__ = [
+    'User', 'UserAction', 'UserGroup', 'Warning_',
+    'UserConfigStart', 'UserConfig',
+    ]
+
 
 class User(ModelSQL, ModelView):
     "User"
-    _name = "res.user"
-    _description = __doc__
+    __name__ = "res.user"
     name = fields.Char('Name', required=True, select=True, translate=True)
     login = fields.Char('Login', required=True)
     login_try = fields.Integer('Login Try', required=True)
@@ -65,19 +70,23 @@
     warnings = fields.One2Many('res.user.warning', 'user', 'Warnings')
     sessions = fields.Function(fields.Integer('Sessions'),
             'get_sessions')
+    _get_preferences_cache = Cache('res_user.get_preferences')
+    _get_groups_cache = Cache('res_user.get_groups')
+    _get_login_cache = Cache('res_user._get_login', context=False)
 
-    def __init__(self):
-        super(User, self).__init__()
-        self._rpc.update({
-            'get_preferences': False,
-            'set_preferences': True,
-            'get_preferences_fields_view': False,
-        })
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(User, cls).__setup__()
+        cls.__rpc__.update({
+                'get_preferences': RPC(),
+                'set_preferences': RPC(readonly=False),
+                'get_preferences_fields_view': RPC(),
+                })
+        cls._sql_constraints += [
             ('login_key', 'UNIQUE (login)',
                 'You can not have two users with the same login!')
         ]
-        self._preferences_fields = [
+        cls._preferences_fields = [
             'name',
             'password',
             'email',
@@ -88,13 +97,13 @@
             'status_bar',
             'warnings',
         ]
-        self._context_fields = [
+        cls._context_fields = [
             'language',
             'language_direction',
             'timezone',
             'groups',
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'rm_root': 'You can not remove the root user\n' \
                             'as it is used internally for resources\n' \
                             'created by the system ' \
@@ -102,9 +111,10 @@
             'wrong_password': 'Wrong password!',
             })
 
-    def init(self, module_name):
-        super(User, self).init(module_name)
-        table = TableHandler(Transaction().cursor, self, module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(User, cls).__register__(module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
 
         # Migration from 1.6
 
@@ -124,68 +134,61 @@
         # Migration from 2.2
         table.not_null_action('menu', action='remove')
 
-    def default_login_try(self):
+    @staticmethod
+    def default_login_try():
         return 0
 
-    def default_password(self):
+    @staticmethod
+    def default_password():
         return None
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_menu(self):
+    @staticmethod
+    def default_menu():
         pool = Pool()
-        action_obj = pool.get('ir.action')
-        action_ids = action_obj.search([
+        Action = pool.get('ir.action')
+        actions = Action.search([
             ('usage', '=', 'menu'),
             ], limit=1)
-        if action_ids:
-            return action_ids[0]
+        if actions:
+            return actions[0].id
         return None
 
-    def get_pyson_menu(self, ids, name):
+    def get_pyson_menu(self, name):
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
+        encoder = PYSONEncoder()
+        return encoder.encode(
+            Action.get_action_values(self.menu.type, [self.menu.id])[0])
 
-        encoder = PYSONEncoder()
-        result = {}
-        for user in self.browse(ids):
-            result[user.id] = encoder.encode(
-                action_obj.get_action_values(user.menu.type, user.menu.id))
-        return result
+    def get_language_direction(self, name):
+        pool = Pool()
+        Lang = pool.get('ir.lang')
+        if self.language:
+            return self.language.direction
+        else:
+            return Lang.default_direction()
 
-    def get_language_direction(self, ids, name):
-        res = {}
-        pool = Pool()
-        lang_obj = pool.get('ir.lang')
-        default_direction = lang_obj.default_direction()
-        for user in self.browse(ids):
-            if user.language:
-                res[user.id] = user.language.direction
-            else:
-                res[user.id] = default_direction
-        return res
+    def get_status_bar(self, name):
+        return self.name
 
-    def get_status_bar(self, ids, name):
-        res = {}
-        for user in self.browse(ids):
-            res[user.id] = user.name
-        return res
-
-    def get_sessions(self, ids, name):
-        session_obj = Pool().get('ir.session')
+    @staticmethod
+    def get_sessions(users, name):
+        Session = Pool().get('ir.session')
         cursor = Transaction().cursor
         now = datetime.datetime.now()
         timeout = datetime.timedelta(seconds=int(CONFIG['session_timeout']))
-        result = dict((i, 0) for i in ids)
-        for i in range(0, len(ids), cursor.IN_MAX):
-            sub_ids = ids[i:i + cursor.IN_MAX]
+        result = dict((u.id, 0) for u in users)
+        for i in range(0, len(users), cursor.IN_MAX):
+            sub_ids = [u.id for u in users[i:i + cursor.IN_MAX]]
 
             with Transaction().set_user(0):
-                session_ids = session_obj.search([
+                sessions = Session.search([
                         ('create_uid', 'in', sub_ids),
                         ], order=[('create_uid', 'ASC')])
-                sessions = session_obj.browse(session_ids)
 
             def filter_(session):
                 timestamp = session.write_date or session.create_date
@@ -195,12 +198,13 @@
                         attrgetter('create_uid.id'))))
         return result
 
-    def _convert_vals(self, vals):
+    @staticmethod
+    def _convert_vals(vals):
         vals = vals.copy()
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
         if 'menu' in vals:
-            vals['menu'] = action_obj.get_action_id(vals['menu'])
+            vals['menu'] = Action.get_action_id(vals['menu'])
         if 'password' in vals:
             if vals['password'] == 'x' * 10:
                 del vals['password']
@@ -210,122 +214,111 @@
                 vals['password'] += vals['salt']
         return vals
 
-    def create(self, vals):
-        vals = self._convert_vals(vals)
-        res = super(User, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        vals = cls._convert_vals(vals)
+        res = super(User, cls).create(vals)
         # Restart the cache for _get_login
-        self._get_login.reset()
+        cls._get_login_cache.clear()
         return res
 
-    def write(self, ids, vals):
-        vals = self._convert_vals(vals)
-        res = super(User, self).write(ids, vals)
+    @classmethod
+    def write(cls, users, vals):
+        vals = cls._convert_vals(vals)
+        super(User, cls).write(users, vals)
         # Restart the cache for domain_get method
         pool = Pool()
-        pool.get('ir.rule').domain_get.reset()
+        pool.get('ir.rule')._domain_get_cache.clear()
         # Restart the cache for get_groups
-        self.get_groups.reset()
+        cls._get_groups_cache.clear()
         # Restart the cache for _get_login
-        self._get_login.reset()
+        cls._get_login_cache.clear()
         # Restart the cache for get_preferences
-        self.get_preferences.reset()
+        cls._get_preferences_cache.clear()
         # Restart the cache of check
-        pool.get('ir.model.access').check.reset()
+        pool.get('ir.model.access')._get_access_cache.clear()
         # Restart the cache
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
+        ModelView._fields_view_get_cache.clear()
+
+    @classmethod
+    def delete(cls, users):
+        if [u.id for u in users if u.id == 0]:
+            cls.raise_user_error('rm_root')
+        super(User, cls).delete(users)
+        # Restart the cache for _get_login
+        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
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        if 0 in ids:
-            self.raise_user_error('rm_root')
-        res = super(User, self).delete(ids)
-        # Restart the cache for _get_login
-        self._get_login.reset()
-        return res
-
-    def read(self, ids, fields_names=None):
-        res = super(User, self).read(ids, fields_names=fields_names)
-        if isinstance(ids, (int, long)):
-            if 'password' in res:
-                res['password'] = 'x' * 10
-        else:
-            for val in res:
-                if 'password' in val:
-                    val['password'] = 'x' * 10
-        return res
-
-    def search_rec_name(self, name, clause):
-        ids = self.search([
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        users = cls.search([
             ('login', '=', clause[2]),
             ], order=[])
-        if len(ids) == 1:
-            return [('id', '=', ids[0])]
-        return [(self._rec_name,) + tuple(clause[1:])]
+        if len(users) == 1:
+            return [('id', '=', users[0].id)]
+        return [(cls._rec_name,) + tuple(clause[1:])]
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, users, default=None):
         if default is None:
             default = {}
         default = default.copy()
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
         default['password'] = ''
 
-        new_ids = []
-        for user in self.browse(ids):
+        new_users = []
+        for user in users:
             default['login'] = user.login + ' (copy)'
-            new_id = super(User, self).copy(user.id, default)
-            new_ids.append(new_id)
+            new_user, = super(User, cls).copy([user], default)
+            new_users.append(new_user)
+        return new_users
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-    def _get_preferences(self, user, context_only=False):
+    @classmethod
+    def _get_preferences(cls, user, context_only=False):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        action_obj = pool.get('ir.action')
-        config_item = pool.get('ir.module.module.config_wizard.item')
+        ModelData = pool.get('ir.model.data')
+        Action = pool.get('ir.action')
+        ConfigItem = pool.get('ir.module.module.config_wizard.item')
 
         res = {}
         if context_only:
-            fields = self._context_fields
+            fields = cls._context_fields
         else:
-            fields = self._preferences_fields + self._context_fields
+            fields = cls._preferences_fields + cls._context_fields
         for field in fields:
-            if self._columns[field]._type in ('many2one',):
+            if cls._fields[field]._type in ('many2one',):
                 if field == 'language':
                     if user.language:
                         res['language'] = user.language.code
                     else:
                         res['language'] = CONFIG['language']
                 else:
-                    res[field] = user[field].id
-                    if user[field].id:
-                        res[field + '.rec_name'] = user[field].rec_name
-            elif self._columns[field]._type in ('one2many', 'many2many'):
-                res[field] = [x.id for x in user[field]]
+                    res[field] = None
+                    if getattr(user, field):
+                        res[field] = getattr(user, field).id
+                        res[field + '.rec_name'] = \
+                            getattr(user, field).rec_name
+            elif cls._fields[field]._type in ('one2many', 'many2many'):
+                res[field] = [x.id for x in getattr(user, field)]
                 if field == 'actions' and user.login == 'admin':
-                    config_wizard_id = model_data_obj.get_id('ir',
+                    config_wizard_id = ModelData.get_id('ir',
                         'act_module_config_wizard')
-                    action_id = action_obj.get_action_id(config_wizard_id)
+                    action_id = Action.get_action_id(config_wizard_id)
                     if action_id in res[field]:
                         res[field].remove(action_id)
-                    if config_item.search([
+                    if ConfigItem.search([
                                 ('state', '=', 'open'),
                                 ]):
                         res[field].insert(0, action_id)
             else:
-                res[field] = user[field]
+                res[field] = getattr(user, field)
 
         if user.language:
             date = user.language.date
@@ -340,52 +333,57 @@
             }
         return res
 
-    @Cache('res_user.get_preferences')
-    def get_preferences(self, context_only=False):
+    @classmethod
+    def get_preferences(cls, context_only=False):
+        key = (Transaction().user, context_only)
+        preferences = cls._get_preferences_cache.get(key)
+        if preferences is not None:
+            return preferences
         user = Transaction().user
         with Transaction().set_user(0):
-            user = self.browse(user)
-        return self._get_preferences(user, context_only=context_only)
+            user = cls(user)
+        preferences = cls._get_preferences(user, context_only=context_only)
+        cls._get_preferences_cache.set(key, preferences)
+        return preferences
 
-    def set_preferences(self, values, old_password=False):
+    @classmethod
+    def set_preferences(cls, values, old_password=False):
         '''
         Set user preferences.
-
-        :param values: a dictionary with values
-        :param old_password: the previous password if password is in values
         '''
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
+        Lang = pool.get('ir.lang')
         values_clean = values.copy()
-        fields = self._preferences_fields + self._context_fields
+        fields = cls._preferences_fields + cls._context_fields
         user_id = Transaction().user
+        with Transaction().set_user(0):
+            user = cls(user_id)
         for field in values:
             if field not in fields or field == 'groups':
                 del values_clean[field]
             if field == 'password':
-                with Transaction().set_user(0):
-                    user = self.browse(user_id)
-                    if not self.get_login(user.login, old_password):
-                        self.raise_user_error('wrong_password')
+                if not cls.get_login(user.login, old_password):
+                    cls.raise_user_error('wrong_password')
             if field == 'language':
-                lang_ids = lang_obj.search([
+                langs = Lang.search([
                     ('code', '=', values['language']),
                     ])
-                if lang_ids:
-                    values_clean['language'] = lang_ids[0]
+                if langs:
+                    values_clean['language'] = langs[0].id
                 else:
                     del values_clean['language']
         with Transaction().set_user(0):
-            self.write(user_id, values_clean)
+            cls.write([user], values_clean)
 
-    def get_preferences_fields_view(self):
+    @classmethod
+    def get_preferences_fields_view(cls):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        lang_obj = pool.get('ir.lang')
-        action_obj = pool.get('ir.action')
+        ModelData = pool.get('ir.model.data')
+        Lang = pool.get('ir.lang')
+        Action = pool.get('ir.action')
 
-        view_id = model_data_obj.get_id('res', 'user_view_form_preferences')
-        res = self.fields_view_get(view_id=view_id)
+        view_id = ModelData.get_id('res', 'user_view_form_preferences')
+        res = cls.fields_view_get(view_id=view_id)
         res = copy.deepcopy(res)
         for field in res['fields']:
             if field not in ('groups', 'language_direction'):
@@ -402,57 +400,61 @@
 
         if 'language' in res['fields']:
             selection = convert2selection(res['fields'], 'language')
-            lang_ids = lang_obj.search(['OR',
+            langs = Lang.search(['OR',
                     ('translatable', '=', True),
                     ('code', '=', CONFIG['language']),
                     ])
+            lang_ids = [l.id for l in langs]
             with Transaction().set_context(translate_name=True):
-                for lang in lang_obj.browse(lang_ids):
+                for lang in Lang.browse(lang_ids):
                     selection.append((lang.code, lang.name))
         if 'action' in res['fields']:
             selection = convert2selection(res['fields'], 'action')
             selection.append((None, ''))
-            action_ids = action_obj.search([])
-            for action in action_obj.browse(action_ids):
+            actions = Action.search([])
+            for action in actions:
                 selection.append((action.id, action.rec_name))
         if 'menu' in res['fields']:
             selection = convert2selection(res['fields'], 'menu')
-            action_ids = action_obj.search([
+            actions = Action.search([
                     ('usage', '=', 'menu'),
                     ])
-            for action in action_obj.browse(action_ids):
+            for action in actions:
                 selection.append((action.id, action.rec_name))
         return res
 
-    @Cache('res_user.get_groups')
-    def get_groups(self):
+    @classmethod
+    def get_groups(cls):
         '''
         Return a list of group ids for the user
+        '''
+        user = Transaction().user
+        groups = cls._get_groups_cache.get(user)
+        if groups is not None:
+            return groups
+        groups = cls.read([Transaction().user], ['groups'])[0]['groups']
+        cls._get_groups_cache.set(user, groups)
+        return groups
 
-        :return: a list of group ids
-        '''
-        return self.read(Transaction().user, ['groups'])['groups']
-
-    @Cache('res_user._get_login')
-    def _get_login(self, login):
+    @classmethod
+    def _get_login(cls, login):
+        result = cls._get_groups_cache.get(login)
+        if result:
+            return result
         cursor = Transaction().cursor
         cursor.execute('SELECT id, password, salt ' \
-                'FROM "' + self._table + '" '
+                'FROM "' + cls._table + '" '
                 'WHERE login = %s AND active', (login,))
-        res = cursor.fetchone()
-        if not res:
-            return None, None, None
-        return res
+        result = cursor.fetchone() or (None, None, None)
+        cls._get_groups_cache.set(login, result)
+        return result
 
-    def get_login(self, login, password):
+    @classmethod
+    def get_login(cls, login, password):
         '''
         Return user id if password matches
-
-        :param login: the login name
-        :param password: the password
-        :return: integer
         '''
-        user_id, user_password, salt = self._get_login(login)
+        user_id, user_password, salt = cls._get_login(login)
         if not user_id:
             return 0
         password += salt or ''
@@ -464,118 +466,101 @@
             password_sha = sha.new(password).hexdigest()
         cursor = Transaction().cursor
         if password_sha == user_password:
-            cursor.execute('UPDATE "' + self._table + '" '
+            cursor.execute('UPDATE "' + cls._table + '" '
                 'SET login_try = 0 '
                 'WHERE login = %s', (login,))
             return user_id
-        cursor.execute('UPDATE "' + self._table + '" '
+        cursor.execute('UPDATE "' + cls._table + '" '
             'SET login_try = login_try + 1 '
             'WHERE login = %s', (login,))
-        cursor.execute('SELECT login_try FROM "' + self._table + '" '
+        cursor.execute('SELECT login_try FROM "' + cls._table + '" '
             'WHERE login = %s', (login,))
         login_try, = cursor.fetchone()
         time.sleep(2 ** login_try)
         return 0
 
-User()
-
 
 class UserAction(ModelSQL):
     'User - Action'
-    _name = 'res.user-ir.action'
+    __name__ = 'res.user-ir.action'
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE', select=True,
         required=True)
     action = fields.Many2One('ir.action', 'Action', ondelete='CASCADE',
         select=True, required=True)
 
-    def _convert_values(self, values):
+    @staticmethod
+    def _convert_values(values):
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
         values = values.copy()
         if values.get('action'):
-            values['action'] = action_obj.get_action_id(values['action'])
+            values['action'] = Action.get_action_id(values['action'])
         return values
 
-    def create(self, values):
-        values = self._convert_values(values)
-        return super(UserAction, self).create(values)
+    @classmethod
+    def create(cls, values):
+        values = cls._convert_values(values)
+        return super(UserAction, cls).create(values)
 
-    def write(self, ids, values):
-        values = self._convert_values(values)
-        return super(UserAction, self).write(ids, values)
-
-UserAction()
+    @classmethod
+    def write(cls, records, values):
+        values = cls._convert_values(values)
+        super(UserAction, cls).write(records, values)
 
 
 class UserGroup(ModelSQL):
     'User - Group'
-    _name = 'res.user-res.group'
-    _description = __doc__
+    __name__ = 'res.user-res.group'
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE', select=True,
             required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'res_group_user_rel', self._table)
+        TableHandler.table_rename(cursor, 'res_group_user_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'res_group_user_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 uid and gid rename into user and group
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('uid', 'user')
         table.column_rename('gid', 'group')
-        super(UserGroup, self).init(module_name)
+        super(UserGroup, cls).__register__(module_name)
 
-UserGroup()
 
-
-class Group(ModelSQL, ModelView):
-    "Group"
-    _name = "res.group"
-    users = fields.Many2Many('res.user-res.group', 'group', 'user', 'Users')
-
-Group()
-
-
-class Warning(ModelSQL, ModelView):
+class Warning_(ModelSQL, ModelView):
     'User Warning'
-    _name = 'res.user.warning'
-    _description = __doc__
+    __name__ = 'res.user.warning'
 
     user = fields.Many2One('res.user', 'User', required=True, select=True)
     name = fields.Char('Name', required=True, select=True)
     always = fields.Boolean('Always')
 
-    def check(self, warning_name):
+    @classmethod
+    def check(cls, warning_name):
         user = Transaction().user
         if not user:
             return False
-        warning_ids = self.search([
+        warnings = cls.search([
             ('user', '=', user),
             ('name', '=', warning_name),
             ])
-        if not warning_ids:
+        if not warnings:
             return True
-        warnings = self.browse(warning_ids)
-        self.delete([x.id for x in warnings if not x.always])
+        cls.delete([x.id for x in warnings if not x.always])
         return False
 
-Warning()
-
 
 class UserConfigStart(ModelView):
     'User Config Init'
-    _name = 'res.user.config.start'
-    _description = __doc__
-
-UserConfigStart()
+    __name__ = 'res.user.config.start'
 
 
 class UserConfig(Wizard):
     'Configure users'
-    _name = 'res.user.config'
+    __name__ = 'res.user.config'
 
     start = StateView('res.user.config.start',
         'res.user_config_start_view_form', [
@@ -589,20 +574,6 @@
             ])
     add = StateTransition()
 
-    def transition_add(self, session):
-        pool = Pool()
-        user_obj = pool.get('res.user')
-        values = session.data['user'].copy()
-        for fname in values.keys():
-            if fname == 'id':
-                del values[fname]
-                continue
-            field = user_obj._columns[fname]
-            if field._type == 'one2many':
-                values[fname] = [('create', v) for v in values[fname]]
-            elif field._type == 'many2many':
-                values[fname] = [('set', [v['id'] for v in values[fname]])]
-        user_obj.create(values)
+    def transition_add(self):
+        self.user.save()
         return 'user'
-
-UserConfig()
diff -r fb51f5b4aa4c trytond/rpc.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trytond/rpc.py	Mon Sep 24 20:36:36 2012 +0200
@@ -0,0 +1,45 @@
+#This file is part of Tryton.  The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+
+__all__ = ['RPC']
+
+
+class RPC(object):
+    '''Define RPC behavior
+
+    readonly: The transaction mode
+    instantiate: The position of the argument to be instanciated
+    result: The function to transform the result
+    '''
+
+    __slots__ = ('readonly', 'instantiate', 'result')
+
+    def __init__(self, readonly=True, instantiate=None, result=None):
+        self.readonly = readonly
+        self.instantiate = instantiate
+        if result is None:
+            result = lambda r: r
+        self.result = result
+
+    def convert(self, obj, *args, **kwargs):
+        args = list(args)
+        if 'context' in kwargs:
+            context = kwargs.pop('context')
+        else:
+            context = args.pop()
+        timestamp = None
+        if '_timestamp' in context:
+            timestamp = context['_timestamp']
+            del context['_timestamp']
+        if self.instantiate is not None:
+
+            def instance(data):
+                if isinstance(data, (int, long)):
+                    return obj(data)
+                elif isinstance(data, dict):
+                    return obj(**data)
+                else:
+                    return obj.browse(data)
+            data = args[self.instantiate]
+            args[self.instantiate] = instance(data)
+        return args, kwargs, context, timestamp
diff -r fb51f5b4aa4c trytond/security.py
--- a/trytond/security.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/security.py	Mon Sep 24 20:36:36 2012 +0200
@@ -17,16 +17,16 @@
 def login(dbname, loginname, password, cache=True):
     with Transaction().start(dbname, 0) as transaction:
         pool = _get_pool(dbname)
-        user_obj = pool.get('res.user')
+        User = pool.get('res.user')
         password = password.decode('utf-8')
-        user_id = user_obj.get_login(loginname, password)
+        user_id = User.get_login(loginname, password)
         transaction.cursor.commit()
     if user_id:
         if not cache:
             return user_id
         with Transaction().start(dbname, user_id) as transaction:
-            session_obj = pool.get('ir.session')
-            session = session_obj.browse(session_obj.create({}))
+            Session = pool.get('ir.session')
+            session = Session.create({})
             transaction.cursor.commit()
             return user_id, session.key
     return False
@@ -35,13 +35,12 @@
 def logout(dbname, user, session):
     with Transaction().start(dbname, 0) as transaction:
         pool = _get_pool(dbname)
-        session_obj = pool.get('ir.session')
-        session_id, = session_obj.search([
+        Session = pool.get('ir.session')
+        session, = Session.search([
                 ('key', '=', session),
                 ])
-        session = session_obj.browse(session_id)
         name = session.create_uid.login
-        session_obj.delete(session_id)
+        Session.delete([session])
         transaction.cursor.commit()
     return name
 
@@ -60,9 +59,9 @@
         raise NotLogged()
     with Transaction().start(dbname, user) as transaction:
         pool = _get_pool(dbname)
-        session_obj = pool.get('ir.session')
+        Session = pool.get('ir.session')
         try:
-            if not session_obj.check(user, session):
+            if not Session.check(user, session):
                 raise NotLogged()
             else:
                 return user
diff -r fb51f5b4aa4c trytond/server.py
--- a/trytond/server.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/server.py	Mon Sep 24 20:36:36 2012 +0200
@@ -189,11 +189,11 @@
                     try:
                         if 'ir.cron' not in pool.object_name_list():
                             continue
-                        cron_obj = pool.get('ir.cron')
+                        Cron = pool.get('ir.cron')
                     finally:
                         pool.lock.release()
                     thread = threading.Thread(
-                            target=cron_obj.run,
+                            target=Cron.run,
                             args=(dbname,), kwargs={})
                     thread.start()
                     threads[dbname] = thread
diff -r fb51f5b4aa4c trytond/test/__init__.py
--- a/trytond/test/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,6 +1,6 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-
+from ..pool import Pool
 from .test import *
 from .model import *
 from .mptt import *
@@ -10,3 +10,113 @@
 from .access import *
 from .wizard import *
 from .workflow import *
+
+
+def register():
+    Pool.register(
+        Boolean,
+        BooleanDefault,
+        Integer,
+        IntegerDefault,
+        IntegerRequired,
+        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,
+        One2One,
+        One2OneTarget,
+        One2OneRelation,
+        One2OneRequired,
+        One2OneRequiredRelation,
+        One2Many,
+        One2ManyTarget,
+        One2ManyRequired,
+        One2ManyRequiredTarget,
+        One2ManyReference,
+        One2ManyReferenceTarget,
+        One2ManySize,
+        One2ManySizeTarget,
+        One2ManySizePYSON,
+        One2ManySizePYSONTarget,
+        Many2Many,
+        Many2ManyTarget,
+        Many2ManyRelation,
+        Many2ManyRequired,
+        Many2ManyRequiredTarget,
+        Many2ManyRequiredRelation,
+        Many2ManyReference,
+        Many2ManyReferenceTarget,
+        Many2ManyReferenceRelation,
+        Many2ManySize,
+        Many2ManySizeTarget,
+        Many2ManySizeRelation,
+        Reference,
+        ReferenceTarget,
+        ReferenceRequired,
+        Property,
+        Singleton,
+        URLObject,
+        ModelInherits,
+        ModelInheritsSubModel,
+        ModelInheritsSubSubModel,
+        ModelInheritsSubSubSubModel,
+        ModelInheritsOverriddenFieldModel,
+        MPTT,
+        ImportDataBoolean,
+        ImportDataInteger,
+        ImportDataFloat,
+        ImportDataNumeric,
+        ImportDataChar,
+        ImportDataText,
+        ImportDataSha,
+        ImportDataDate,
+        ImportDataDateTime,
+        ImportDataSelection,
+        ImportDataMany2OneTarget,
+        ImportDataMany2One,
+        ImportDataMany2ManyTarget,
+        ImportDataMany2Many,
+        ImportDataMany2ManyRelation,
+        ImportDataOne2Many,
+        ImportDataOne2ManyTarget,
+        ImportDataReferenceSelection,
+        ImportDataReference,
+        ExportDataTarget,
+        ExportData,
+        ExportDataRelation,
+        Triggered,
+        TestAccess,
+        TestWizardStart,
+        WorkflowedModel,
+        module='test', type_='model')
+    Pool.register(
+        TestWizard,
+        module='test', type_='wizard')
diff -r fb51f5b4aa4c trytond/test/access.py
--- a/trytond/test/access.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/access.py	Mon Sep 24 20:36:36 2012 +0200
@@ -2,13 +2,13 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelSQL, fields
 
+__all__ = [
+    'TestAccess',
+    ]
+
 
 class TestAccess(ModelSQL):
     'Test Access'
-    _name = 'test.access'
-    _description = __doc__
-
+    __name__ = 'test.access'
     field1 = fields.Char('Field 1')
     field2 = fields.Char('Field 2')
-
-TestAccess()
diff -r fb51f5b4aa4c trytond/test/export_data.py
--- a/trytond/test/export_data.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/export_data.py	Mon Sep 24 20:36:36 2012 +0200
@@ -3,20 +3,20 @@
 "Test for export_data"
 from trytond.model import ModelSQL, fields
 
+__all__ = [
+    'ExportDataTarget', 'ExportData', 'ExportDataRelation',
+    ]
+
 
 class ExportDataTarget(ModelSQL):
     "Export Data Target"
-    _name = 'test.export_data.target'
-    _description = __doc__
+    __name__ = 'test.export_data.target'
     name = fields.Char('Name')
 
-ExportDataTarget()
-
 
 class ExportData(ModelSQL):
     "Export Data"
-    _name = 'test.export_data'
-    _description = __doc__
+    __name__ = 'test.export_data'
     boolean = fields.Boolean('Boolean')
     integer = fields.Integer('Integer')
     float = fields.Float('Float')
@@ -40,20 +40,15 @@
         ('test.export_data.target', 'Target'),
         ])
 
-ExportData()
-
 
 class ExportDataTarget(ExportDataTarget):
+    'Export Date Target'
+    __name__ = 'test.export_data.target'
     one2many = fields.Many2One('test.export_data', 'Export Data')
 
-ExportDataTarget()
-
 
 class ExportDataRelation(ModelSQL):
     "Export Data Many2Many"
-    _name = 'test.export_data.relation'
-    _description = __doc__
+    __name__ = 'test.export_data.relation'
     many2many = fields.Many2One('test.export_data', 'Export Data')
     target = fields.Many2One('test.export_data.target', 'Target')
-
-ExportDataRelation()
diff -r fb51f5b4aa4c trytond/test/import_data.py
--- a/trytond/test/import_data.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/import_data.py	Mon Sep 24 20:36:36 2012 +0200
@@ -3,182 +3,138 @@
 "Test for import_data"
 from trytond.model import ModelSQL, fields
 
+__all__ = [
+    'ImportDataBoolean', 'ImportDataInteger', 'ImportDataFloat',
+    'ImportDataNumeric', 'ImportDataChar', 'ImportDataText', 'ImportDataSha',
+    'ImportDataDate', 'ImportDataDateTime', 'ImportDataSelection',
+    'ImportDataMany2OneTarget', 'ImportDataMany2One',
+    'ImportDataMany2ManyTarget', 'ImportDataMany2Many',
+    'ImportDataMany2ManyRelation', 'ImportDataOne2Many',
+    'ImportDataOne2ManyTarget', 'ImportDataReferenceSelection',
+    'ImportDataReference',
+    ]
+
 
 class ImportDataBoolean(ModelSQL):
     "Import Data Boolean"
-    _name = 'test.import_data.boolean'
-    _description = __doc__
+    __name__ = 'test.import_data.boolean'
     boolean = fields.Boolean('Boolean')
 
-ImportDataBoolean()
-
 
 class ImportDataInteger(ModelSQL):
     "Import Data Integer"
-    _name = 'test.import_data.integer'
-    _description = __doc__
+    __name__ = 'test.import_data.integer'
     integer = fields.Integer('Integer')
 
-ImportDataInteger()
-
 
 class ImportDataFloat(ModelSQL):
     "Import Data Float"
-    _name = 'test.import_data.float'
-    _description = __doc__
+    __name__ = 'test.import_data.float'
     float = fields.Float('Float')
 
-ImportDataFloat()
-
 
 class ImportDataNumeric(ModelSQL):
     "Import Data Numeric"
-    _name = 'test.import_data.numeric'
-    _description = __doc__
+    __name__ = 'test.import_data.numeric'
     numeric = fields.Numeric('Numeric')
 
-ImportDataNumeric()
-
 
 class ImportDataChar(ModelSQL):
     "Import Data Char"
-    _name = 'test.import_data.char'
-    _description = __doc__
+    __name__ = 'test.import_data.char'
     char = fields.Char('Char')
 
-ImportDataChar()
-
 
 class ImportDataText(ModelSQL):
     "Import Data Text"
-    _name = 'test.import_data.text'
-    _description = __doc__
+    __name__ = 'test.import_data.text'
     text = fields.Text('Text')
 
-ImportDataText()
-
 
 class ImportDataSha(ModelSQL):
     "Import Data Sha"
-    _name = 'test.import_data.sha'
-    _description = __doc__
+    __name__ = 'test.import_data.sha'
     sha = fields.Sha('Sha')
 
-ImportDataSha()
-
 
 class ImportDataDate(ModelSQL):
     "Import Data Date"
-    _name = 'test.import_data.date'
-    _description = __doc__
+    __name__ = 'test.import_data.date'
     date = fields.Date('Date')
 
-ImportDataDate()
-
 
 class ImportDataDateTime(ModelSQL):
     "Import Data DateTime"
-    _name = 'test.import_data.datetime'
-    _description = __doc__
+    __name__ = 'test.import_data.datetime'
     datetime = fields.DateTime('DateTime')
 
-ImportDataDateTime()
-
 
 class ImportDataSelection(ModelSQL):
     "Import Data Selection"
-    _name = 'test.import_data.selection'
-    _description = __doc__
+    __name__ = 'test.import_data.selection'
     selection = fields.Selection([
         ('select1', 'Select 1'),
         ('select2', 'Select 2'),
         ], 'Selection')
 
-ImportDataSelection()
-
 
 class ImportDataMany2OneTarget(ModelSQL):
     "Import Data Many2One Target"
-    _name = 'test.import_data.many2one.target'
-    _description = __doc__
+    __name__ = 'test.import_data.many2one.target'
     name = fields.Char('Name')
 
-ImportDataMany2OneTarget()
-
 
 class ImportDataMany2One(ModelSQL):
     "Import Data Many2One"
-    _name = 'test.import_data.many2one'
-    _description = __doc__
+    __name__ = 'test.import_data.many2one'
     many2one = fields.Many2One('test.import_data.many2one.target',
             'Many2One')
 
-ImportDataMany2One()
-
 
 class ImportDataMany2ManyTarget(ModelSQL):
     "Import Data Many2Many Target"
-    _name = 'test.import_data.many2many.target'
-    _description = __doc__
+    __name__ = 'test.import_data.many2many.target'
     name = fields.Char('Name')
 
-ImportDataMany2ManyTarget()
-
 
 class ImportDataMany2Many(ModelSQL):
     "Import Data Many2Many"
-    _name = 'test.import_data.many2many'
-    _description = __doc__
+    __name__ = 'test.import_data.many2many'
     many2many = fields.Many2Many('test.import_data.many2many.relation',
             'many2many', 'target', 'Many2Many')
 
-ImportDataMany2Many()
-
 
 class ImportDataMany2ManyRelation(ModelSQL):
     "Import Data Many2Many Relation"
-    _name = 'test.import_data.many2many.relation'
+    __name__ = 'test.import_data.many2many.relation'
     many2many = fields.Many2One('test.import_data.many2many', 'Many2One')
     target = fields.Many2One('test.import_data.many2many.target', 'Target')
 
-ImportDataMany2ManyRelation()
-
 
 class ImportDataOne2Many(ModelSQL):
     "Import Data One2Many"
-    _name = 'test.import_data.one2many'
-    _description = __doc__
+    __name__ = 'test.import_data.one2many'
     name = fields.Char('Name')
     one2many = fields.One2Many('test.import_data.one2many.target', 'one2many',
             'One2Many')
 
-ImportDataOne2Many()
-
 
 class ImportDataOne2ManyTarget(ModelSQL):
     "Import Data One2Many Target"
-    _name = 'test.import_data.one2many.target'
-    _description = __doc__
+    __name__ = 'test.import_data.one2many.target'
     name = fields.Char('Name')
     one2many = fields.Many2One('test.import_data.one2many', 'One2Many')
 
-ImportDataOne2ManyTarget()
-
 
 class ImportDataReferenceSelection(ModelSQL):
     "Import Data Reference Selection"
-    _name = 'test.import_data.reference.selection'
-    _description = __doc__
+    __name__ = 'test.import_data.reference.selection'
     name = fields.Char('Name')
 
-ImportDataReferenceSelection()
-
 
 class ImportDataReference(ModelSQL):
     "Import Data Reference"
-    _name = 'test.import_data.reference'
+    __name__ = 'test.import_data.reference'
     reference = fields.Reference('Reference', [
         ('test.import_data.reference.selection', 'Test'),
         ])
-
-ImportDataReference()
diff -r fb51f5b4aa4c trytond/test/model.py
--- a/trytond/test/model.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/model.py	Mon Sep 24 20:36:36 2012 +0200
@@ -2,76 +2,71 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelSingleton, ModelSQL, fields
 
+__all__ = [
+    'Singleton', 'URLObject', 'ModelInherits', 'ModelInheritsSubModel',
+    'ModelInheritsSubSubModel', 'ModelInheritsSubSubSubModel',
+    'ModelInheritsOverriddenFieldModel',
+    ]
+
 
 class Singleton(ModelSingleton, ModelSQL):
     'Singleton'
-    _name = 'test.singleton'
-    _description = __doc__
+    __name__ = 'test.singleton'
+    name = fields.Char('Name')
+
+    @staticmethod
+    def default_name():
+        return 'test'
+
+
+class URLObject(ModelSQL):
+    'URLObject'
+    __name__ = 'test.urlobject'
+    name = fields.Char('Name')
+
+
+class ModelInherits(ModelSQL):
+    'Model Inherits'
+    __name__ = 'test.model_inherits'
 
     name = fields.Char('Name')
 
-    def default_name(self):
-        return 'test'
-
-Singleton()
-
-
-class URLObject(ModelSQL):
-    _name = 'test.urlobject'
-
-    name = fields.Char('Name')
-
-
-URLObject()
-
-
-class ModelInherits(ModelSQL):
-    _name = 'test.model_inherits'
-
-    name = fields.Char('Name')
-
-ModelInherits()
-
 
 class ModelInheritsSubModel(ModelSQL):
-    _name = 'test.submodel'
+    'Model Inherits Sub-Model'
+    __name__ = 'test.submodel'
     _inherits = {'test.model_inherits': 'parent_model'}
 
     subfield = fields.Char('SubField')
     parent_model = fields.Many2One('test.model_inherits', 'Parent model',
         required=True)
 
-ModelInheritsSubModel()
-
 
 class ModelInheritsSubSubModel(ModelSQL):
-    _name = 'test.subsubmodel'
+    'Model Inherits Sub-Sub-Model'
+    __name__ = 'test.subsubmodel'
     _inherits = {'test.submodel': 'parent_model'}
 
     subsubfield = fields.Char('SubField')
     parent_model = fields.Many2One('test.submodel', 'Parent model',
         required=True)
 
-ModelInheritsSubSubModel()
-
 
 class ModelInheritsSubSubSubModel(ModelSQL):
-    _name = 'test.subsubsubmodel'
+    'Model Inherits Sub-Sub-Sub-Model'
+    __name__ = 'test.subsubsubmodel'
     _inherits = {'test.subsubmodel': 'parent_model'}
 
     subsubsubfield = fields.Char('SubField')
     parent_model = fields.Many2One('test.subsubmodel', 'Parent model',
         required=True)
 
-ModelInheritsSubSubSubModel()
-
 
 class ModelInheritsOverriddenFieldModel(ModelSQL):
-    _name = 'test.overriddeninheritedfieldmodel'
+    'Model Inherits Overridden Field Model'
+    __name__ = 'test.overriddeninheritedfieldmodel'
     _inherits = {'test.subsubmodel': 'parent_model'}
 
     subfield = fields.Integer('Overridden field')
     parent_model = fields.Many2One('test.subsubmodel', 'Parent model',
         required=True)
-
-ModelInheritsOverriddenFieldModel()
diff -r fb51f5b4aa4c trytond/test/mptt.py
--- a/trytond/test/mptt.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/mptt.py	Mon Sep 24 20:36:36 2012 +0200
@@ -3,11 +3,14 @@
 "Test for Tree"
 from trytond.model import ModelView, ModelSQL, fields
 
+__all__ = [
+    'MPTT',
+    ]
+
 
 class MPTT(ModelSQL, ModelView):
     'Modified Preorder Tree Traversal'
-    _name = 'test.mptt'
-    _description = __doc__
+    __name__ = 'test.mptt'
     name = fields.Char('Name', required=True)
     sequence = fields.Integer('Sequence',
         order_field='(%(table)s.sequence IS NULL) %(order)s, '
@@ -19,23 +22,25 @@
     childs = fields.One2Many('test.mptt', 'parent', 'Children')
     active = fields.Boolean('Active')
 
-    def __init__(self):
-        super(MPTT, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(MPTT, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._constraints += [
             ('check_recursion', 'recursive_mptt'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_mptt': 'You can not create recursive Tree!',
         })
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_left(self):
+    @staticmethod
+    def default_left():
         return 0
 
-    def default_right(self):
+    @staticmethod
+    def default_right():
         return 0
-
-MPTT()
diff -r fb51f5b4aa4c trytond/test/test.py
--- a/trytond/test/test.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/test.py	Mon Sep 24 20:36:36 2012 +0200
@@ -6,738 +6,573 @@
 from trytond.model import ModelSQL, fields
 from trytond.pyson import Eval
 
+__all__ = [
+    'Boolean', 'BooleanDefault',
+    'Integer', 'IntegerDefault', 'IntegerRequired',
+    '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',
+    'One2One', 'One2OneTarget', 'One2OneRelation', 'One2OneRequired',
+    'One2OneRequiredRelation',
+    'One2Many', 'One2ManyTarget',
+    'One2ManyRequired', 'One2ManyRequiredTarget',
+    'One2ManyReference', 'One2ManyReferenceTarget',
+    'One2ManySize', 'One2ManySizeTarget',
+    'One2ManySizePYSON', 'One2ManySizePYSONTarget',
+    'Many2Many', 'Many2ManyTarget', 'Many2ManyRelation',
+    'Many2ManyRequired', 'Many2ManyRequiredTarget',
+    'Many2ManyRequiredRelation',
+    'Many2ManyReference', 'Many2ManyReferenceTarget',
+    'Many2ManyReferenceRelation',
+    'Many2ManySize', 'Many2ManySizeTarget', 'Many2ManySizeRelation',
+    'Reference', 'ReferenceTarget', 'ReferenceRequired',
+    'Property',
+    ]
+
 
 class Boolean(ModelSQL):
     'Boolean'
-    _name = 'test.boolean'
-    _description = __doc__
+    __name__ = 'test.boolean'
     boolean = fields.Boolean(string='Boolean', help='Test boolean')
 
-Boolean()
-
 
 class BooleanDefault(ModelSQL):
     'Boolean Default'
-    _name = 'test.boolean_default'
-    _description = __doc__
+    __name__ = 'test.boolean_default'
     boolean = fields.Boolean(string='Boolean', help='Test boolean')
 
-    def default_boolean(self):
+    @staticmethod
+    def default_boolean():
         return True
 
-BooleanDefault()
-
 
 class Integer(ModelSQL):
     'Integer'
-    _name = 'test.integer'
-    _description = __doc__
+    __name__ = 'test.integer'
     integer = fields.Integer(string='Integer', help='Test integer',
             required=False)
 
-Integer()
-
 
 class IntegerDefault(ModelSQL):
     'Integer Default'
-    _name = 'test.integer_default'
-    _description = __doc__
+    __name__ = 'test.integer_default'
     integer = fields.Integer(string='Integer', help='Test integer',
             required=False)
 
-    def default_integer(self):
+    @staticmethod
+    def default_integer():
         return 5
 
-IntegerDefault()
-
 
 class IntegerRequired(ModelSQL):
     'Integer Required'
-    _name = 'test.integer_required'
-    _description = __doc__
+    __name__ = 'test.integer_required'
     integer = fields.Integer(string='Integer', help='Test integer',
             required=True)
 
-IntegerRequired()
-
 
 class Float(ModelSQL):
     'Float'
-    _name = 'test.float'
-    _description = __doc__
+    __name__ = 'test.float'
     float = fields.Float(string='Float', help='Test float',
             required=False)
 
-Float()
-
 
 class FloatDefault(ModelSQL):
     'Float Default'
-    _name = 'test.float_default'
-    _description = __doc__
+    __name__ = 'test.float_default'
     float = fields.Float(string='Float', help='Test float',
             required=False)
 
-    def default_float(self):
+    @staticmethod
+    def default_float():
         return 5.5
 
-FloatDefault()
-
 
 class FloatRequired(ModelSQL):
     'Float Required'
-    _name = 'test.float_required'
-    _description = __doc__
+    __name__ = 'test.float_required'
     float = fields.Float(string='Float', help='Test float',
             required=True)
 
-FloatRequired()
-
 
 class FloatDigits(ModelSQL):
     'Float Digits'
-    _name = 'test.float_digits'
-    _description = __doc__
+    __name__ = 'test.float_digits'
     digits = fields.Integer('Digits')
     float = fields.Float(string='Float', help='Test float',
         required=False, digits=(16, Eval('digits', 2)),
         depends=['digits'])
 
-FloatDigits()
-
 
 class Numeric(ModelSQL):
     'Numeric'
-    _name = 'test.numeric'
-    _description = __doc__
+    __name__ = 'test.numeric'
     numeric = fields.Numeric(string='Numeric', help='Test numeric',
             required=False)
 
-Numeric()
-
 
 class NumericDefault(ModelSQL):
     'Numeric Default'
-    _name = 'test.numeric_default'
-    _description = __doc__
+    __name__ = 'test.numeric_default'
     numeric = fields.Numeric(string='Numeric', help='Test numeric',
             required=False)
 
-    def default_numeric(self):
+    @staticmethod
+    def default_numeric():
         return Decimal('5.5')
 
-NumericDefault()
-
 
 class NumericRequired(ModelSQL):
     'Numeric Required'
-    _name = 'test.numeric_required'
-    _description = __doc__
+    __name__ = 'test.numeric_required'
     numeric = fields.Numeric(string='Numeric', help='Test numeric',
             required=True)
 
-NumericRequired()
-
 
 class NumericDigits(ModelSQL):
     'Numeric Digits'
-    _name = 'test.numeric_digits'
-    _description = __doc__
+    __name__ = 'test.numeric_digits'
     digits = fields.Integer('Digits')
     numeric = fields.Numeric(string='Numeric', help='Test numeric',
         required=False, digits=(16, Eval('digits', 2)),
         depends=['digits'])
 
-NumericDigits()
-
 
 class Char(ModelSQL):
     'Char'
-    _name = 'test.char'
-    _description = __doc__
+    __name__ = 'test.char'
     char = fields.Char(string='Char', size=None, help='Test char',
             required=False)
 
-Char()
-
 
 class CharDefault(ModelSQL):
     'Char Default'
-    _name = 'test.char_default'
-    _description = __doc__
+    __name__ = 'test.char_default'
     char = fields.Char(string='Char', size=None, help='Test char',
             required=False)
 
-    def default_char(self):
+    @staticmethod
+    def default_char():
         return 'Test'
 
-CharDefault()
-
 
 class CharRequired(ModelSQL):
     'Char Required'
-    _name = 'test.char_required'
-    _description = __doc__
+    __name__ = 'test.char_required'
     char = fields.Char(string='Char', size=None, help='Test char',
             required=True)
 
-CharRequired()
-
 
 class CharSize(ModelSQL):
     'Char Size'
-    _name = 'test.char_size'
-    _description = __doc__
+    __name__ = 'test.char_size'
     char = fields.Char(string='Char', size=5, help='Test char',
             required=False)
 
-CharSize()
-
 
 class CharTranslate(ModelSQL):
     'Char Translate'
-    _name = 'test.char_translate'
-    _description = __doc__
+    __name__ = 'test.char_translate'
     char = fields.Char(string='Char', size=None, help='Test char',
             required=False, translate=True)
 
-CharTranslate()
-
 
 class Text(ModelSQL):
     'Text'
-    _name = 'test.text'
-    _description = __doc__
+    __name__ = 'test.text'
     text = fields.Text(string='Text', size=None, help='Test text',
             required=False)
 
-Text()
-
 
 class TextDefault(ModelSQL):
     'Text Default'
-    _name = 'test.text_default'
-    _description = __doc__
+    __name__ = 'test.text_default'
     text = fields.Text(string='Text', size=None, help='Test text',
             required=False)
 
-    def default_text(self):
+    @staticmethod
+    def default_text():
         return 'Test'
 
-TextDefault()
-
 
 class TextRequired(ModelSQL):
     'Text Required'
-    _name = 'test.text_required'
-    _description = __doc__
+    __name__ = 'test.text_required'
     text = fields.Text(string='Text', size=None, help='Test text',
             required=True)
 
-TextRequired()
-
 
 class TextSize(ModelSQL):
     'Text Size'
-    _name = 'test.text_size'
-    _description = __doc__
+    __name__ = 'test.text_size'
     text = fields.Text(string='Text', size=5, help='Test text',
             required=False)
 
-TextSize()
-
 
 class TextTranslate(ModelSQL):
     'Text Translate'
-    _name = 'test.text_translate'
-    _description = __doc__
+    __name__ = 'test.text_translate'
     text = fields.Text(string='Text', size=None, help='Test text',
             required=False, translate=True)
 
-TextTranslate()
-
 
 class Sha(ModelSQL):
     'Sha'
-    _name = 'test.sha'
-    _description = __doc__
+    __name__ = 'test.sha'
     sha = fields.Sha(string='Sha', help='Test sha',
             required=False)
 
-Sha()
-
 
 class ShaDefault(ModelSQL):
     'Sha Default'
-    _name = 'test.sha_default'
-    _description = __doc__
+    __name__ = 'test.sha_default'
     sha = fields.Sha(string='Sha', help='Test sha',
             required=False)
 
-    def default_sha(self):
+    @staticmethod
+    def default_sha():
         return 'Sha'
 
-ShaDefault()
-
 
 class ShaRequired(ModelSQL):
     'Sha Required'
-    _name = 'test.sha_required'
-    _description = __doc__
+    __name__ = 'test.sha_required'
     sha = fields.Sha(string='Sha', help='Test sha',
             required=True)
 
-ShaRequired()
-
 
 class Date(ModelSQL):
     'Date'
-    _name = 'test.date'
-    _description = __doc__
+    __name__ = 'test.date'
     date = fields.Date(string='Date', help='Test date',
             required=False)
 
-Date()
-
 
 class DateDefault(ModelSQL):
     'Date Default'
-    _name = 'test.date_default'
-    _description = __doc__
+    __name__ = 'test.date_default'
     date = fields.Date(string='Date', help='Test date',
             required=False)
 
-    def default_date(self):
+    @staticmethod
+    def default_date():
         return datetime.date(2000, 1, 1)
 
-DateDefault()
-
 
 class DateRequired(ModelSQL):
     'Date Required'
-    _name = 'test.date_required'
-    _description = __doc__
+    __name__ = 'test.date_required'
     date = fields.Date(string='Date', help='Test date',
             required=True)
 
-DateRequired()
-
 
 class DateTime(ModelSQL):
     'DateTime'
-    _name = 'test.datetime'
-    _description = __doc__
+    __name__ = 'test.datetime'
     datetime = fields.DateTime(string='DateTime', help='Test datetime',
             required=False)
 
-DateTime()
-
 
 class DateTimeDefault(ModelSQL):
     'DateTime Default'
-    _name = 'test.datetime_default'
-    _description = __doc__
+    __name__ = 'test.datetime_default'
     datetime = fields.DateTime(string='DateTime', help='Test datetime',
             required=False)
 
-    def default_datetime(self):
+    @staticmethod
+    def default_datetime():
         return datetime.datetime(2000, 1, 1, 12, 0, 0, 0)
 
-DateTimeDefault()
-
 
 class DateTimeRequired(ModelSQL):
     'DateTime Required'
-    _name = 'test.datetime_required'
-    _description = __doc__
+    __name__ = 'test.datetime_required'
     datetime = fields.DateTime(string='DateTime', help='Test datetime',
             required=True)
 
-DateTimeRequired()
-
 
 class DateTimeFormat(ModelSQL):
     'DateTime Format'
-    _name = 'test.datetime_format'
-    _description = __doc__
+    __name__ = 'test.datetime_format'
     datetime = fields.DateTime(string='DateTime', format='%H:%M')
 
-DateTimeFormat()
-
 
 class Time(ModelSQL):
     'Time'
-    _name = 'test.time'
-    _description = __doc__
+    __name__ = 'test.time'
     time = fields.Time(string='Time', help='Test time', required=False)
 
-Time()
-
 
 class TimeDefault(ModelSQL):
     'Time Default'
-    _name = 'test.time_default'
-    _description = __doc__
+    __name__ = 'test.time_default'
     time = fields.Time(string='Time', help='Test time', required=False)
 
-    def default_time(self):
+    @staticmethod
+    def default_time():
         return datetime.time(16, 30)
 
-TimeDefault()
-
 
 class TimeRequired(ModelSQL):
     'Time'
-    _name = 'test.time_required'
-    _description = __doc__
+    __name__ = 'test.time_required'
     time = fields.Time(string='Time', help='Test time', required=True)
 
-TimeRequired()
-
 
 class TimeFormat(ModelSQL):
     'Time Format'
-    _name = 'test.time_format'
-    _description = __doc__
+    __name__ = 'test.time_format'
     time = fields.Time(string='Time', format='%H:%M')
 
-TimeFormat()
-
 
 class One2One(ModelSQL):
     'One2One'
-    _name = 'test.one2one'
-    _description = __doc__
+    __name__ = 'test.one2one'
     name = fields.Char('Name', required=True)
     one2one = fields.One2One('test.one2one.relation', 'origin', 'target',
             string='One2One', help='Test one2one', required=False)
 
-One2One()
-
 
 class One2OneTarget(ModelSQL):
     'One2One Target'
-    _name = 'test.one2one.target'
+    __name__ = 'test.one2one.target'
     name = fields.Char('Name', required=True)
 
-One2OneTarget()
-
 
 class One2OneRelation(ModelSQL):
     'One2One Relation'
-    _name = 'test.one2one.relation'
-    _description = __doc__
+    __name__ = 'test.one2one.relation'
     origin = fields.Many2One('test.one2one', 'Origin')
     target = fields.Many2One('test.one2one.target', 'Target')
 
-    def __init__(self):
-        super(One2OneRelation, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(One2OneRelation, cls).__setup__()
+        cls._sql_constraints += [
             ('origin_unique', 'UNIQUE(origin)',
                 'Origin must be unique'),
             ('target_unique', 'UNIQUE(target)',
                 'Target must be unique'),
-        ]
-
-One2OneRelation()
+            ]
 
 
 class One2OneRequired(ModelSQL):
     'One2One'
-    _name = 'test.one2one_required'
-    _description = __doc__
+    __name__ = 'test.one2one_required'
     name = fields.Char('Name', required=True)
     one2one = fields.One2One('test.one2one_required.relation', 'origin',
         'target', string='One2One', help='Test one2one', required=True)
 
-One2OneRequired()
-
 
 class One2OneRequiredRelation(ModelSQL):
     'One2One Relation'
-    _name = 'test.one2one_required.relation'
-    _description = __doc__
+    __name__ = 'test.one2one_required.relation'
     origin = fields.Many2One('test.one2one_required', 'Origin')
     target = fields.Many2One('test.one2one.target', 'Target')
 
-    def __init__(self):
-        super(One2OneRequiredRelation, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(One2OneRequiredRelation, cls).__setup__()
+        cls._sql_constraints += [
             ('origin_unique', 'UNIQUE(origin)',
                 'Origin must be unique'),
             ('target_unique', 'UNIQUE(target)',
                 'Target must be unique'),
-        ]
-
-One2OneRequiredRelation()
+            ]
 
 
 class One2Many(ModelSQL):
     'One2Many Relation'
-    _name = 'test.one2many'
-    _description = __doc__
+    __name__ = 'test.one2many'
     name = fields.Char('Name', required=True)
     targets = fields.One2Many('test.one2many.target', 'origin', 'Targets')
 
-One2Many()
-
 
 class One2ManyTarget(ModelSQL):
     'One2Many Target'
-    _name = 'test.one2many.target'
-    _description = __doc__
+    __name__ = 'test.one2many.target'
     name = fields.Char('Name', required=True)
     origin = fields.Many2One('test.one2many', 'Origin')
 
-One2ManyTarget()
-
 
 class One2ManyRequired(ModelSQL):
     'One2Many Required'
-    _name = 'test.one2many_required'
-    _description = __doc__
+    __name__ = 'test.one2many_required'
     name = fields.Char('Name', required=True)
     targets = fields.One2Many('test.one2many_required.target', 'origin',
         'Targets', required=True)
 
-One2ManyRequired()
-
 
 class One2ManyRequiredTarget(ModelSQL):
     'One2Many Required Target'
-    _name = 'test.one2many_required.target'
-    _description = __doc__
+    __name__ = 'test.one2many_required.target'
     name = fields.Char('Name', required=True)
     origin = fields.Many2One('test.one2many_required', 'Origin')
 
-One2ManyRequiredTarget()
-
 
 class One2ManyReference(ModelSQL):
     'One2Many Reference Relation'
-    _name = 'test.one2many_reference'
-    _description = __doc__
+    __name__ = 'test.one2many_reference'
     name = fields.Char('Name', required=True)
     targets = fields.One2Many('test.one2many_reference.target', 'origin',
         'Targets')
 
-One2ManyReference()
-
 
 class One2ManyReferenceTarget(ModelSQL):
     'One2Many Reference Target'
-    _name = 'test.one2many_reference.target'
-    _description = __doc__
+    __name__ = 'test.one2many_reference.target'
     name = fields.Char('Name', required=True)
     origin = fields.Reference('Origin', [
             ('test.one2many_reference', 'One2Many Reference'),
             ])
 
-One2ManyReferenceTarget()
-
 
 class One2ManySize(ModelSQL):
     'One2Many Size Relation'
-    _name = 'test.one2many_size'
-    _description = __doc__
+    __name__ = 'test.one2many_size'
     targets = fields.One2Many('test.one2many_size.target', 'origin', 'Targets',
         size=3)
 
-One2ManySize()
-
 
 class One2ManySizeTarget(ModelSQL):
     'One2Many Size Target'
-    _name = 'test.one2many_size.target'
-    _description = __doc__
+    __name__ = 'test.one2many_size.target'
     origin = fields.Many2One('test.one2many_size', 'Origin')
 
-One2ManySizeTarget()
-
 
 class One2ManySizePYSON(ModelSQL):
     'One2Many Size PYSON Relation'
-    _name = 'test.one2many_size_pyson'
-    _description = __doc__
+    __name__ = 'test.one2many_size_pyson'
     limit = fields.Integer('Limit')
     targets = fields.One2Many('test.one2many_size_pyson.target', 'origin',
         'Targets', size=Eval('limit', 0))
 
-One2ManySizePYSON()
-
 
 class One2ManySizePYSONTarget(ModelSQL):
     'One2Many Size PYSON Target'
-    _name = 'test.one2many_size_pyson.target'
-    _description = __doc__
+    __name__ = 'test.one2many_size_pyson.target'
     origin = fields.Many2One('test.one2many_size_pyson', 'Origin')
 
-One2ManySizePYSONTarget()
-
 
 class Many2Many(ModelSQL):
     'Many2Many'
-    _name = 'test.many2many'
-    _description = __doc__
+    __name__ = 'test.many2many'
     name = fields.Char('Name', required=True)
     targets = fields.Many2Many('test.many2many.relation', 'origin', 'target',
         'Targets')
 
-Many2Many()
-
 
 class Many2ManyTarget(ModelSQL):
     'Many2Many Target'
-    _name = 'test.many2many.target'
-    _description = __doc__
+    __name__ = 'test.many2many.target'
     name = fields.Char('Name', required=True)
 
 
-Many2ManyTarget()
-
-
 class Many2ManyRelation(ModelSQL):
     'Many2Many Relation'
-    _name = 'test.many2many.relation'
-    _description = __doc__
+    __name__ = 'test.many2many.relation'
     origin = fields.Many2One('test.many2many', 'Origin')
     target = fields.Many2One('test.many2many.target', 'Target')
 
-Many2ManyRelation()
-
 
 class Many2ManyRequired(ModelSQL):
     'Many2Many Required'
-    _name = 'test.many2many_required'
-    _description = __doc__
+    __name__ = 'test.many2many_required'
     name = fields.Char('Name', required=True)
     targets = fields.Many2Many('test.many2many_required.relation', 'origin',
         'target', 'Targets', required=True)
 
-Many2ManyRequired()
-
 
 class Many2ManyRequiredTarget(ModelSQL):
     'Many2Many Required Target'
-    _name = 'test.many2many_required.target'
-    _description = __doc__
+    __name__ = 'test.many2many_required.target'
     name = fields.Char('Name', required=True)
 
 
-Many2ManyRequiredTarget()
-
-
 class Many2ManyRequiredRelation(ModelSQL):
     'Many2Many Required Relation'
-    _name = 'test.many2many_required.relation'
-    _description = __doc__
+    __name__ = 'test.many2many_required.relation'
     origin = fields.Many2One('test.many2many_required', 'Origin')
     target = fields.Many2One('test.many2many_required.target', 'Target')
 
-Many2ManyRequiredRelation()
-
 
 class Many2ManyReference(ModelSQL):
     'Many2Many Reference'
-    _name = 'test.many2many_reference'
-    _description = __doc__
+    __name__ = 'test.many2many_reference'
     name = fields.Char('Name', required=True)
     targets = fields.Many2Many('test.many2many_reference.relation', 'origin',
         'target', 'Targets')
 
-Many2ManyReference()
-
 
 class Many2ManyReferenceTarget(ModelSQL):
     'Many2Many Reference Target'
-    _name = 'test.many2many_reference.target'
-    _description = __doc__
+    __name__ = 'test.many2many_reference.target'
     name = fields.Char('Name', required=True)
 
 
-Many2ManyReferenceTarget()
-
-
 class Many2ManyReferenceRelation(ModelSQL):
     'Many2Many Relation'
-    _name = 'test.many2many_reference.relation'
-    _description = __doc__
+    __name__ = 'test.many2many_reference.relation'
     origin = fields.Reference('Origin', [
             ('test.many2many_reference', 'Many2Many Reference'),
             ])
     target = fields.Many2One('test.many2many_reference.target',
         'Reference Target')
 
-Many2ManyReferenceRelation()
-
 
 class Many2ManySize(ModelSQL):
     'Many2Many Size Relation'
-    _name = 'test.many2many_size'
-    _description = __doc__
+    __name__ = 'test.many2many_size'
     targets = fields.Many2Many('test.many2many_size.relation', 'origin',
         'target', 'Targets', size=5)
 
-Many2ManySize()
-
 
 class Many2ManySizeTarget(ModelSQL):
     'Many2Many Size Target'
-    _name = 'test.many2many_size.target'
-    _description = __doc__
+    __name__ = 'test.many2many_size.target'
     name = fields.Char('Name')
 
-Many2ManySizeTarget()
-
 
 class Many2ManySizeRelation(ModelSQL):
     'Many2Many Size Relation'
-    _name = 'test.many2many_size.relation'
-    _description = __doc__
+    __name__ = 'test.many2many_size.relation'
     origin = fields.Many2One('test.many2many_size', 'Origin')
     target = fields.Many2One('test.many2many_size.target', 'Target')
 
-Many2ManySizeRelation()
-
 
 class Reference(ModelSQL):
     'Reference'
-    _name = 'test.reference'
-    _description = __doc__
+    __name__ = 'test.reference'
     name = fields.Char('Name', required=True)
     reference = fields.Reference('Reference', selection=[
             ('test.reference.target', 'Target'),
             ])
 
-Reference()
-
 
 class ReferenceTarget(ModelSQL):
     'Reference Target'
-    _name = 'test.reference.target'
-    _description = __doc__
+    __name__ = 'test.reference.target'
     name = fields.Char('Name', required=True)
 
-ReferenceTarget()
-
 
 class ReferenceRequired(ModelSQL):
     'Reference Required'
-    _name = 'test.reference_required'
-    _description = __doc__
+    __name__ = 'test.reference_required'
     name = fields.Char('Name', required=True)
     reference = fields.Reference('Reference', selection=[
             ('test.reference.target', 'Target'),
             ], required=True)
 
-ReferenceRequired()
-
 
 class Property(ModelSQL):
     'Property'
-    _name = 'test.property'
-    _description = __doc__
-
+    __name__ = 'test.property'
     char = fields.Property(fields.Char('Test Char'))
     many2one = fields.Property(fields.Many2One('test.char',
             'Test Many2One'))
@@ -746,5 +581,3 @@
                 ('option_a', 'Option A'),
                 ('option_b', 'Option B')
             ], 'Test Selection'))
-
-Property()
diff -r fb51f5b4aa4c trytond/test/trigger.py
--- a/trytond/test/trigger.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/trigger.py	Mon Sep 24 20:36:36 2012 +0200
@@ -2,20 +2,21 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelSQL, fields
 
+__all__ = [
+    'Triggered',
+    ]
+
 TRIGGER_LOGS = []
 
 
 class Triggered(ModelSQL):
     'Triggered'
-    _name = 'test.triggered'
-    _description = __doc__
-
+    __name__ = 'test.triggered'
     name = fields.Char('Name')
 
-    def trigger(self, ids, trigger_id):
+    @staticmethod
+    def trigger(records, trigger):
         '''
         Trigger function for test
         '''
-        TRIGGER_LOGS.append((ids, trigger_id))
-
-Triggered()
+        TRIGGER_LOGS.append((records, trigger))
diff -r fb51f5b4aa4c trytond/test/wizard.py
--- a/trytond/test/wizard.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/wizard.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,26 +1,30 @@
 #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 ModelView, fields
+from trytond.model import ModelView, ModelSQL, fields
 from trytond.wizard import Wizard, StateView, StateTransition, StateAction, \
     Button
 from trytond.transaction import Transaction
 
+__all__ = [
+    'TestWizardStart', 'TestWizard',
+    ]
 
-class TestWizardStart(ModelView):
-    _name = 'test.test_wizard.start'
+
+class TestWizardStart(ModelSQL, ModelView):
+    'Test Wizard'
+    __name__ = 'test.test_wizard.start'
     name = fields.Char('Test me')
     user = fields.Many2One('res.user', 'User')
     groups = fields.One2Many('res.group', None, 'Groups')
 
-    def default_user(self):
+    @staticmethod
+    def default_user():
         return Transaction().user
 
-TestWizardStart()
-
 
 class TestWizard(Wizard):
-    _name = 'test.test_wizard'
-
+    'Test Wizard'
+    __name__ = 'test.test_wizard'
     start = StateView('test.test_wizard.start',
         'test.test_wizard_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -29,18 +33,20 @@
     next_ = StateTransition()
     action = StateAction('ir.act_menu_tree')
 
-    def default_start(self, session, fields):
+    @staticmethod
+    def default_start(fields):
         return {
             'name': 'Test wizard',
             }
 
-    def transition_next_(self, session):
+    @staticmethod
+    def transition_next_():
         return 'action'
 
-    def do_action(self, session, action):
+    @staticmethod
+    def do_action(action):
         return action, {}
 
-    def transition_action(self, session):
+    @staticmethod
+    def transition_action():
         return 'end'
-
-TestWizard()
diff -r fb51f5b4aa4c trytond/test/workflow.py
--- a/trytond/test/workflow.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/test/workflow.py	Mon Sep 24 20:36:36 2012 +0200
@@ -3,27 +3,32 @@
 
 from trytond.model import ModelSQL, Workflow, fields
 
+__all__ = [
+    'WorkflowedModel',
+    ]
+
 
 class WorkflowedModel(Workflow, ModelSQL):
-    _name = 'test.workflowed'
-
+    'Workflowed Model'
+    __name__ = 'test.workflowed'
     state = fields.Selection([
             ('start', 'Start'),
             ('running', 'Running'),
             ('end', 'End'),
             ], 'State')
 
-    def __init__(self):
-        super(WorkflowedModel, self).__init__()
-        self._transitions |= set((
+    @classmethod
+    def __setup__(cls):
+        super(WorkflowedModel, cls).__setup__()
+        cls._transitions |= set((
                 ('start', 'running'),
                 ))
 
-    def default_state(self):
+    @staticmethod
+    def default_state(cls):
         return 'start'
 
+    @classmethod
     @Workflow.transition('running')
-    def run(self, ids):
+    def run(cls, records):
         pass
-
-WorkflowedModel()
diff -r fb51f5b4aa4c trytond/tests/test_access.py
--- a/trytond/tests/test_access.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_access.py	Mon Sep 24 20:36:36 2012 +0200
@@ -26,82 +26,79 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id, = self.model.search([('model', '=', 'test.access')])
+            model, = self.model.search([('model', '=', 'test.access')])
 
-            test_id = self.test_access.create({})
+            test = self.test_access.create({})
 
             # Without model access
-            self.test_access.read(test_id)
+            self.test_access.read([test.id])
 
             # With model access
 
             # One access allowed for any group
-            model_access_wo_group_id = self.model_access.create({
-                'model': model_id,
-                'group': False,
-                'perm_read': True,
-                })
-            self.test_access.read(test_id)
+            model_access_wo_group = self.model_access.create({
+                    'model': model.id,
+                    'group': False,
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id])
 
             # One access disallowed for any group
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_read': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.read,
-                    test_id)
+            self.model_access.write([model_access_wo_group], {
+                    'perm_read': False,
+                    })
+            self.assertRaises(Exception, self.test_access.read, [test.id])
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            model_access_w_group_id = self.model_access.create({
-                'model': model_id,
-                'group': group_id,
-                'perm_read': True,
-                })
+            group, = self.group.search([('users', '=', USER)])
+            model_access_w_group = self.model_access.create({
+                    'model': model.id,
+                    'group': group.id,
+                    'perm_read': True,
+                    })
 
-            self.test_access.read(test_id)
+            self.test_access.read([test.id])
 
             # Two access rules with both allowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_read': True,
-                })
-            self.test_access.read(test_id)
+            self.model_access.write([model_access_wo_group], {
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id])
 
             # Two access rules with any group allowed
-            self.model_access.write(model_access_w_group_id, {
-                'perm_read': False,
-                })
-            self.test_access.read(test_id)
+            self.model_access.write([model_access_w_group], {
+                    'perm_read': False,
+                    })
+            self.test_access.read([test.id])
 
             # Two access rules with both disallowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_read': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.read,
-                    test_id)
+            self.model_access.write([model_access_wo_group], {
+                    'perm_read': False,
+                    })
+            self.assertRaises(Exception, self.test_access.read, [test.id])
 
             # One access disallowed for one group
-            self.model_access.delete(model_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.read,
-                    test_id)
+            self.model_access.delete([model_access_wo_group])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
 
             # One access allowed for one group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_read': True,
-                })
-            self.test_access.read(test_id)
+            self.model_access.write([model_access_w_group], {
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id])
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.model_access.write(model_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.read(test_id)
+            group = self.group.create({'name': 'Test'})
+            self.model_access.write([model_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.read([test.id])
 
             # One access disallowed for one other group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_read': False,
-                })
-            self.test_access.read(test_id)
+            self.model_access.write([model_access_w_group], {
+                    'perm_read': False,
+                    })
+            self.test_access.read([test.id])
 
             transaction.cursor.rollback()
 
@@ -111,82 +108,78 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id, = self.model.search([('model', '=', 'test.access')])
+            model, = self.model.search([('model', '=', 'test.access')])
 
-            test_id = self.test_access.create({})
+            test = self.test_access.create({})
 
             # Without model access
-            self.test_access.write(test_id, {})
+            self.test_access.write([test], {})
 
             # With model access
 
             # One access allowed for any group
-            model_access_wo_group_id = self.model_access.create({
-                'model': model_id,
-                'group': False,
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
+            model_access_wo_group = self.model_access.create({
+                    'model': model.id,
+                    'group': False,
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
 
             # One access disallowed for any group
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_write': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.write,
-                    test_id, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_write': False,
+                    })
+            self.assertRaises(Exception, self.test_access.write, [test], {})
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            model_access_w_group_id = self.model_access.create({
-                'model': model_id,
-                'group': group_id,
-                'perm_write': True,
-                })
-
-            self.test_access.write(test_id, {})
+            group, = self.group.search([('users', '=', USER)])
+            model_access_w_group = self.model_access.create({
+                    'model': model.id,
+                    'group': group.id,
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
 
             # Two access rules with both allowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
 
             # Two access rules with any group allowed
-            self.model_access.write(model_access_w_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
+            self.model_access.write([model_access_w_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
 
             # Two access rules with both disallowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_write': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.write,
-                    test_id, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_write': False,
+                    })
+            self.assertRaises(Exception, self.test_access.write, [test], {})
 
             # One access disallowed for one group
-            self.model_access.delete(model_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.write,
-                    test_id, {})
+            self.model_access.delete([model_access_wo_group])
+            self.assertRaises(Exception, self.test_access.write, [test], {})
 
             # One access allowed for one group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
+            self.model_access.write([model_access_w_group], {
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.model_access.write(model_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.write(test_id, {})
+            group = self.group.create({'name': 'Test'})
+            self.model_access.write([model_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.write([test], {})
 
             # One access disallowed for one other group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
+            self.model_access.write([model_access_w_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
 
             transaction.cursor.rollback()
 
@@ -196,7 +189,7 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id, = self.model.search([('model', '=', 'test.access')])
+            model, = self.model.search([('model', '=', 'test.access')])
 
             # Without model access
             self.test_access.create({})
@@ -204,68 +197,68 @@
             # With model access
 
             # One access allowed for any group
-            model_access_wo_group_id = self.model_access.create({
-                'model': model_id,
-                'group': False,
-                'perm_create': True,
-                })
+            model_access_wo_group = self.model_access.create({
+                    'model': model.id,
+                    'group': False,
+                    'perm_create': True,
+                    })
             self.test_access.create({})
 
             # One access disallowed for any group
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_create': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.create, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_create': False,
+                    })
+            self.assertRaises(Exception, self.test_access.create, {})
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            model_access_w_group_id = self.model_access.create({
-                'model': model_id,
-                'group': group_id,
-                'perm_create': True,
-                })
+            group, = self.group.search([('users', '=', USER)])
+            model_access_w_group = self.model_access.create({
+                    'model': model.id,
+                    'group': group.id,
+                    'perm_create': True,
+                    })
 
             self.test_access.create({})
 
             # Two access rules with both allowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_create': True,
-                })
+            self.model_access.write([model_access_wo_group], {
+                    'perm_create': True,
+                    })
             self.test_access.create({})
 
             # Two access rules with any group allowed
-            self.model_access.write(model_access_w_group_id, {
-                'perm_create': False,
-                })
+            self.model_access.write([model_access_w_group], {
+                    'perm_create': False,
+                    })
             self.test_access.create({})
 
             # Two access rules with both disallowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_create': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.create, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_create': False,
+                    })
+            self.assertRaises(Exception, self.test_access.create, {})
 
             # One access disallowed for one group
-            self.model_access.delete(model_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.create, {})
+            self.model_access.delete([model_access_wo_group])
+            self.assertRaises(Exception, self.test_access.create, {})
 
             # One access allowed for one group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_create': True,
-                })
+            self.model_access.write([model_access_w_group], {
+                    'perm_create': True,
+                    })
             self.test_access.create({})
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.model_access.write(model_access_w_group_id, {
-                'group': group_id,
-                })
+            group = self.group.create({'name': 'Test'})
+            self.model_access.write([model_access_w_group], {
+                    'group': group.id,
+                    })
             self.test_access.create({})
 
             # One access disallowed for one other group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_create': False,
-                })
+            self.model_access.write([model_access_w_group], {
+                    'perm_create': False,
+                    })
             self.test_access.create({})
 
             transaction.cursor.rollback()
@@ -276,82 +269,82 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id, = self.model.search([('model', '=', 'test.access')])
+            model, = self.model.search([('model', '=', 'test.access')])
 
-            test_ids = [self.test_access.create({}) for x in range(11)]
+            tests = [self.test_access.create({}) for x in range(11)]
 
             # Without model access
-            self.test_access.delete(test_ids.pop())
+            self.test_access.delete([tests.pop()])
 
             # With model access
 
             # One access allowed for any group
-            model_access_wo_group_id = self.model_access.create({
-                'model': model_id,
-                'group': False,
-                'perm_delete': True,
-                })
-            self.test_access.delete(test_ids.pop())
+            model_access_wo_group = self.model_access.create({
+                    'model': model.id,
+                    'group': False,
+                    'perm_delete': True,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # One access disallowed for any group
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_delete': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.delete,
-                    test_ids.pop())
+            self.model_access.write([model_access_wo_group], {
+                    'perm_delete': False,
+                    })
+            self.assertRaises(Exception, self.test_access.delete,
+                [tests.pop()])
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            model_access_w_group_id = self.model_access.create({
-                'model': model_id,
-                'group': group_id,
-                'perm_delete': True,
-                })
+            group = self.group.search([('users', '=', USER)])[0]
+            model_access_w_group = self.model_access.create({
+                    'model': model.id,
+                    'group': group.id,
+                    'perm_delete': True,
+                    })
 
-            self.test_access.delete(test_ids.pop())
+            self.test_access.delete([tests.pop()])
 
             # Two access rules with both allowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_delete': True,
-                })
-            self.test_access.delete(test_ids.pop())
+            self.model_access.write([model_access_wo_group], {
+                    'perm_delete': True,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # Two access rules with any group allowed
-            self.model_access.write(model_access_w_group_id, {
-                'perm_delete': False,
-                })
-            self.test_access.delete(test_ids.pop())
+            self.model_access.write([model_access_w_group], {
+                    'perm_delete': False,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # Two access rules with both disallowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_delete': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.delete,
-                    test_ids.pop())
+            self.model_access.write([model_access_wo_group], {
+                    'perm_delete': False,
+                    })
+            self.assertRaises(Exception, self.test_access.delete,
+                [tests.pop()])
 
             # One access disallowed for one group
-            self.model_access.delete(model_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.delete,
-                    test_ids.pop())
+            self.model_access.delete([model_access_wo_group])
+            self.assertRaises(Exception, self.test_access.delete,
+                [tests.pop()])
 
             # One access allowed for one group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_delete': True,
-                })
-            self.test_access.delete(test_ids.pop())
+            self.model_access.write([model_access_w_group], {
+                    'perm_delete': True,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.model_access.write(model_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.delete(test_ids.pop())
+            group = self.group.create({'name': 'Test'})
+            self.model_access.write([model_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # One access disallowed for one other group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_delete': False,
-                })
-            self.test_access.delete(test_ids.pop())
+            self.model_access.write([model_access_w_group], {
+                    'perm_delete': False,
+                    })
+            self.test_access.delete([tests.pop()])
 
             transaction.cursor.rollback()
 
@@ -374,208 +367,208 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            field1_id, = self.field.search([
-                ('model.model', '=', 'test.access'),
-                ('name', '=', 'field1'),
-                ])
-            field2_id, = self.field.search([
-                ('model.model', '=', 'test.access'),
-                ('name', '=', 'field2'),
-                ])
+            field1, = self.field.search([
+                    ('model.model', '=', 'test.access'),
+                    ('name', '=', 'field1'),
+                    ])
+            field2, = self.field.search([
+                    ('model.model', '=', 'test.access'),
+                    ('name', '=', 'field2'),
+                    ])
 
-            test_id = self.test_access.create({
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            test = self.test_access.create({
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Without field access
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
             # With field access
 
             # One access allowed for any group
-            field_access_wo_group_id = self.field_access.create({
-                'field': field1_id,
-                'group': False,
-                'perm_read': True,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            field_access_wo_group = self.field_access.create({
+                    'field': field1.id,
+                    'group': False,
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
             # One access disallowed for any group
-            self.field_access.write(field_access_wo_group_id, {
+            self.field_access.write([field_access_wo_group], {
+                    'perm_read': False,
+                    })
+
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(Exception, getattr, test, 'field1')
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules with one group allowed
+            group = self.group.search([('users', '=', USER)])[0]
+            field_access_w_group = self.field_access.create({
+                    'field': field1.id,
+                    'group': group.id,
+                    'perm_read': True,
+                    })
+
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules with both allowed
+            self.field_access.write([field_access_wo_group], {
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules with any group allowed
+            self.field_access.write([field_access_w_group], {
+                    'perm_read': False,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules with both disallowed
+            self.field_access.write([field_access_wo_group], {
                 'perm_read': False,
                 })
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(Exception, getattr, test, 'field1')
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            self.failUnlessRaises(Exception, getattr, test_access, 'field1')
-            test_access.field2
+            # One access disallowed for one group
+            self.field_access.delete([field_access_wo_group])
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(Exception, getattr, test, 'field1')
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            field_access_w_group_id = self.field_access.create({
-                'field': field1_id,
-                'group': group_id,
-                'perm_read': True,
-                })
+            # One access allowed for one group
+            self.field_access.write([field_access_w_group], {
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            # One access allowed for one other group
+            group = self.group.create({'name': 'Test'})
+            self.field_access.write([field_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            # Two access rules with both allowed
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_read': True,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            # One access disallowed for one other group
+            self.field_access.write([field_access_w_group], {
+                    'perm_read': False,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            # Two access rules with any group allowed
-            self.field_access.write(field_access_w_group_id, {
+            # Two access rules on both fields allowed
+            self.field_access.delete([field_access_w_group])
+
+            field_access1 = self.field_access.create({
+                    'field': field1.id,
+                    'group': False,
+                    'perm_read': True,
+                    })
+            field_access2 = self.field_access.create({
+                    'field': field2.id,
+                    'group': False,
+                    'perm_read': True,
+                    })
+
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules on both fields one allowed and one disallowed
+            self.field_access.write([field_access2], {
                 'perm_read': False,
                 })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            self.test_access.read([test.id], ['field1'])
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            test.field1
+            self.assertRaises(Exception, getattr, test, 'field2')
             transaction.cursor.cache.clear()
-
-            # Two access rules with both disallowed
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_read': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            self.failUnlessRaises(Exception, getattr, test_access, 'field1')
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # One access disallowed for one group
-            self.field_access.delete(field_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            self.failUnlessRaises(Exception, getattr, test_access, 'field1')
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # One access allowed for one group
-            self.field_access.write(field_access_w_group_id, {
-                'perm_read': True,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.field_access.write(field_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # One access disallowed for one other group
-            self.field_access.write(field_access_w_group_id, {
-                'perm_read': False,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # Two access rules on both fields allowed
-            self.field_access.delete(field_access_w_group_id)
-
-            field_access1 = self.field_access.create({
-                'field': field1_id,
-                'group': False,
-                'perm_read': True,
-                })
-            field_access2 = self.field_access.create({
-                'field': field2_id,
-                'group': False,
-                'perm_read': True,
-                })
-
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # Two access rules on both fields one allowed and one disallowed
-            self.field_access.write(field_access2, {
-                'perm_read': False,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            self.failUnlessRaises(Exception, getattr, test_access, 'field2')
-            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
             # Two access rules on both fields disallowed
-            self.field_access.write(field_access1, {
-                'perm_read': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field1'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            self.failUnlessRaises(Exception, getattr, test_access, 'field1')
-            self.failUnlessRaises(Exception, getattr, test_access, 'field2')
+            self.field_access.write([field_access1], {
+                    'perm_read': False,
+                    })
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field1'])
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(Exception, getattr, test, 'field1')
+            self.assertRaises(Exception, getattr, test, 'field2')
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
             transaction.cursor.rollback()
 
@@ -585,201 +578,201 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            field1_id, = self.field.search([
-                ('model.model', '=', 'test.access'),
-                ('name', '=', 'field1'),
-                ])
-            field2_id, = self.field.search([
-                ('model.model', '=', 'test.access'),
-                ('name', '=', 'field2'),
-                ])
+            field1, = self.field.search([
+                    ('model.model', '=', 'test.access'),
+                    ('name', '=', 'field1'),
+                    ])
+            field2, = self.field.search([
+                    ('model.model', '=', 'test.access'),
+                    ('name', '=', 'field2'),
+                    ])
 
-            test_id = self.test_access.create({
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            test = self.test_access.create({
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Without field access
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
 
             # With field access
 
             # One access allowed for any group
-            field_access_wo_group_id = self.field_access.create({
-                'field': field1_id,
-                'group': False,
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            field_access_wo_group = self.field_access.create({
+                    'field': field1.id,
+                    'group': False,
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # One access disallowed for any group
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_write': False,
-                })
+            self.field_access.write([field_access_wo_group], {
+                    'perm_write': False,
+                    })
 
-            self.test_access.write(test_id, {})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.test_access.write([test], {})
+            self.assertRaises(Exception, self.test_access.write, [test],
+                {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            field_access_w_group_id = self.field_access.create({
-                'field': field1_id,
-                'group': group_id,
-                'perm_write': True,
-                })
+            group = self.group.search([('users', '=', USER)])[0]
+            field_access_w_group = self.field_access.create({
+                    'field': field1.id,
+                    'group': group.id,
+                    'perm_write': True,
+                    })
 
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules with both allowed
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.field_access.write([field_access_wo_group], {
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules with any group allowed
-            self.field_access.write(field_access_w_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.field_access.write([field_access_w_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules with both disallowed
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.field_access.write([field_access_wo_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.assertRaises(Exception, self.test_access.write, [test],
+                {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
 
             # One access disallowed for one group
-            self.field_access.delete(field_access_wo_group_id)
-            self.test_access.write(test_id, {})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'ham'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.field_access.delete([field_access_wo_group])
+            self.test_access.write([test], {})
+            self.assertRaises(Exception, self.test_access.write, [test],
+                {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'ham'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
 
             # One access allowed for one group
-            self.field_access.write(field_access_w_group_id, {
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.field_access.write([field_access_w_group], {
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.field_access.write(field_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            group = self.group.create({'name': 'Test'})
+            self.field_access.write([field_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # One access disallowed for one other group
-            self.field_access.write(field_access_w_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.field_access.write([field_access_w_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules on both fields allowed
-            self.field_access.delete(field_access_w_group_id)
+            self.field_access.delete([field_access_w_group])
 
             field_access1 = self.field_access.create({
-                'field': field1_id,
-                'group': False,
-                'perm_write': True,
-                })
+                    'field': field1.id,
+                    'group': False,
+                    'perm_write': True,
+                    })
             field_access2 = self.field_access.create({
-                'field': field2_id,
-                'group': False,
-                'perm_write': True,
-                })
+                    'field': field2.id,
+                    'group': False,
+                    'perm_write': True,
+                    })
 
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules on both fields one allowed and one disallowed
-            self.field_access.write(field_access2, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field2': 'spam'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.field_access.write([field_access2], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
+                    'field2': 'spam'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
 
             # Two access rules on both fields disallowed
-            self.field_access.write(field_access1, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field1': 'ham'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field2': 'spam'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.field_access.write([field_access1], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.assertRaises(Exception, self.test_access.write, [test], {
+                    'field1': 'ham'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
+                    'field2': 'spam'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
diff -r fb51f5b4aa4c trytond/tests/test_exportdata.py
--- a/trytond/tests/test_exportdata.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_exportdata.py	Mon Sep 24 20:36:36 2012 +0200
@@ -35,20 +35,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'boolean': True,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['boolean']) == [[True]])
+            export1 = self.export_data.create({
+                    'boolean': True,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['boolean']), [[True]])
 
-            export2_id = self.export_data.create({
-                'boolean': False,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['boolean']) == [[False]])
+            export2 = self.export_data.create({
+                    'boolean': False,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['boolean']),
+                [[False]])
 
-            self.assert_(self.export_data.export_data([export1_id,
-                export2_id], ['boolean']) == [[True], [False]])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['boolean']),
+                [[True], [False]])
 
             transaction.cursor.rollback()
 
@@ -58,20 +61,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'integer': 2,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['integer']) == [[2]])
+            export1 = self.export_data.create({
+                    'integer': 2,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['integer']), [[2]])
 
-            export2_id = self.export_data.create({
-                'integer': 0,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['integer']) == [[0]])
+            export2 = self.export_data.create({
+                    'integer': 0,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['integer']), [[0]])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['integer']) == [[2], [0]])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['integer']),
+                [[2], [0]])
 
             transaction.cursor.rollback()
 
@@ -81,20 +85,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'float': 1.1,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['float']) == [[1.1]])
+            export1 = self.export_data.create({
+                    'float': 1.1,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['float']), [[1.1]])
 
-            export2_id = self.export_data.create({
-                'float': 0,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['float']) == [[0]])
+            export2 = self.export_data.create({
+                    'float': 0,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['float']), [[0]])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['float']) == [[1.1], [0]])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['float']),
+                [[1.1], [0]])
 
             transaction.cursor.rollback()
 
@@ -104,20 +109,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'numeric': Decimal('1.1'),
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['numeric']) == [[Decimal('1.1')]])
+            export1 = self.export_data.create({
+                    'numeric': Decimal('1.1'),
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['numeric']),
+                [[Decimal('1.1')]])
 
-            export2_id = self.export_data.create({
-                'numeric': Decimal('0'),
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['numeric']) == [[Decimal('0')]])
+            export2 = self.export_data.create({
+                    'numeric': Decimal('0'),
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['numeric']),
+                [[Decimal('0')]])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['numeric']) == [[Decimal('1.1')], [Decimal('0')]])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['numeric']),
+                [[Decimal('1.1')], [Decimal('0')]])
 
             transaction.cursor.rollback()
 
@@ -127,20 +135,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'char': 'test',
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['char']) == [['test']])
+            export1 = self.export_data.create({
+                    'char': 'test',
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['char']), [['test']])
 
-            export2_id = self.export_data.create({
-                'char': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['char']) == [['']])
+            export2 = self.export_data.create({
+                    'char': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['char']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['char']) == [['test'], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['char']),
+                [['test'], ['']])
 
             transaction.cursor.rollback()
 
@@ -150,20 +159,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'text': 'test',
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['text']) == [['test']])
+            export1 = self.export_data.create({
+                    'text': 'test',
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['text']), [['test']])
 
-            export2_id = self.export_data.create({
-                'text': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['text']) == [['']])
+            export2 = self.export_data.create({
+                    'text': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['text']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['text']) == [['test'], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['text']),
+                [['test'], ['']])
 
             transaction.cursor.rollback()
 
@@ -173,11 +183,12 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'sha': 'Test',
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['sha']) == [['640ab2bae07bedc4c163f679a746f7ab7fb5d1fa']])
+            export1 = self.export_data.create({
+                    'sha': 'Test',
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['sha']),
+                [['640ab2bae07bedc4c163f679a746f7ab7fb5d1fa']])
 
             transaction.cursor.rollback()
 
@@ -187,20 +198,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
+            export1 = self.export_data.create({
                 'date': datetime.date(2010, 1, 1),
                 })
-            self.assert_(self.export_data.export_data([export1_id],
+            self.assert_(self.export_data.export_data([export1],
                 ['date']) == [[datetime.date(2010, 1, 1)]])
 
-            export2_id = self.export_data.create({
-                'date': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['date']) == [['']])
+            export2 = self.export_data.create({
+                    'date': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['date']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['date']) == [[datetime.date(2010, 1, 1)], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['date']),
+                [[datetime.date(2010, 1, 1)], ['']])
 
             transaction.cursor.rollback()
 
@@ -210,21 +222,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'datetime': datetime.datetime(2010, 1, 1, 12, 0, 0),
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['datetime']) == [[datetime.datetime(2010, 1, 1, 12, 0, 0)]])
+            export1 = self.export_data.create({
+                    'datetime': datetime.datetime(2010, 1, 1, 12, 0, 0),
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['datetime']),
+                [[datetime.datetime(2010, 1, 1, 12, 0, 0)]])
 
-            export2_id = self.export_data.create({
-                'datetime': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['datetime']) == [['']])
+            export2 = self.export_data.create({
+                    'datetime': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['datetime']),
+                [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['datetime']) == [[datetime.datetime(2010, 1, 1, 12, 0, 0)],
-                    ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['datetime']),
+                [[datetime.datetime(2010, 1, 1, 12, 0, 0)], ['']])
 
             transaction.cursor.rollback()
 
@@ -234,20 +248,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'selection': 'select1',
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['selection']) == [['select1']])
+            export1 = self.export_data.create({
+                    'selection': 'select1',
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['selection']),
+                [['select1']])
 
-            export2_id = self.export_data.create({
-                'selection': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['selection']) == [['']])
+            export2 = self.export_data.create({
+                    'selection': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['selection']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['selection']) == [['select1'], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['selection']),
+                [['select1'], ['']])
 
             transaction.cursor.rollback()
 
@@ -257,23 +274,27 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target_id = self.export_data_target.create({
-                'name': 'Target Test',
-                })
-            export1_id = self.export_data.create({
-                'many2one': target_id,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['many2one/name']) == [['Target Test']])
+            target = self.export_data_target.create({
+                    'name': 'Target Test',
+                    })
+            export1 = self.export_data.create({
+                    'many2one': target.id,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['many2one/name']),
+                [['Target Test']])
 
-            export2_id = self.export_data.create({
-                'many2one': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['many2one/name']) == [['']])
+            export2 = self.export_data.create({
+                    'many2one': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['many2one/name']),
+                [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['many2one/name']) == [['Target Test'], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['many2one/name']),
+                [['Target Test'], ['']])
 
             transaction.cursor.rollback()
 
@@ -283,34 +304,38 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target1_id = self.export_data_target.create({
-                'name': 'Target 1',
-                })
-            export1_id = self.export_data.create({
-                'many2many': [('set', [target1_id])],
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['many2many/name']) == [['Target 1']])
+            target1 = self.export_data_target.create({
+                    'name': 'Target 1',
+                    })
+            export1 = self.export_data.create({
+                    'many2many': [('set', [target1])],
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['many2many/name']),
+                [['Target 1']])
 
-            target2_id = self.export_data_target.create({
-                'name': 'Target 2',
-                })
-            self.export_data.write(export1_id, {
-                'many2many': [('set', [target1_id, target2_id])],
-                })
-            self.assert_(self.export_data.export_data([export1_id], ['id',
-                'many2many/name']) == [[export1_id, 'Target 1'],
-                ['', 'Target 2']])
+            target2 = self.export_data_target.create({
+                    'name': 'Target 2',
+                    })
+            self.export_data.write([export1], {
+                    'many2many': [('set', [target1.id, target2.id])],
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['id',
+                        'many2many/name']),
+                [[export1.id, 'Target 1'], ['', 'Target 2']])
 
-            export2_id = self.export_data.create({
-                'many2many': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['many2many/name']) == [['']])
+            export2 = self.export_data.create({
+                    'many2many': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['many2many/name']),
+                [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['id', 'many2many/name']) == [[export1_id, 'Target 1'],
-                    ['', 'Target 2'], [export2_id, '']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['id', 'many2many/name']),
+                [[export1.id, 'Target 1'], ['', 'Target 2'], [export2.id, '']])
 
             transaction.cursor.rollback()
 
@@ -320,29 +345,33 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({})
+            export1 = self.export_data.create({})
             self.export_data_target.create({
-                'name': 'Target 1',
-                'one2many': export1_id,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['one2many/name']) == [['Target 1']])
+                    'name': 'Target 1',
+                    'one2many': export1.id,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['one2many/name']),
+                [['Target 1']])
 
             self.export_data_target.create({
-                'name': 'Target 2',
-                'one2many': export1_id,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['id', 'one2many/name']) ==
-                [[export1_id, 'Target 1'], ['', 'Target 2']])
+                    'name': 'Target 2',
+                    'one2many': export1.id,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1],
+                    ['id', 'one2many/name']),
+                [[export1.id, 'Target 1'], ['', 'Target 2']])
 
-            export2_id = self.export_data.create({})
-            self.assert_(self.export_data.export_data([export2_id],
-                ['one2many/name']) == [['']])
+            export2 = self.export_data.create({})
+            self.assertEqual(
+                self.export_data.export_data([export2], ['one2many/name']),
+                [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['id', 'one2many/name']) == [[export1_id, 'Target 1'],
-                    ['', 'Target 2'], [export2_id, '']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['id',
+                        'one2many/name']),
+                [[export1.id, 'Target 1'], ['', 'Target 2'], [export2.id, '']])
 
             transaction.cursor.rollback()
 
@@ -352,23 +381,24 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target1_id = self.export_data_target.create({})
-            export1_id = self.export_data.create({
-                'reference': 'test.export_data.target,%s' % target1_id,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['reference']) ==
-                [['test.export_data.target,%s' % target1_id]])
+            target1 = self.export_data_target.create({})
+            export1 = self.export_data.create({
+                    'reference': str(target1),
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['reference']),
+                [[str(target1)]])
 
-            export2_id = self.export_data.create({
-                'reference': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['reference']) == [['']])
+            export2 = self.export_data.create({
+                    'reference': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['reference']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['reference']) == [['test.export_data.target,%s' % target1_id],
-                    ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['reference']),
+                [[str(target1)], ['']])
 
             transaction.cursor.rollback()
 
diff -r fb51f5b4aa4c trytond/tests/test_fields.py
--- a/trytond/tests/test_fields.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_fields.py	Mon Sep 24 20:36:36 2012 +0200
@@ -99,6 +99,8 @@
         self.reference_required = POOL.get('test.reference_required')
 
         self.property_ = POOL.get('test.property')
+        self.ir_property = POOL.get('ir.property')
+        self.model_field = POOL.get('ir.model.field')
 
     def test0010boolean(self):
         '''
@@ -106,107 +108,97 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            boolean1_id = self.boolean.create({
-                'boolean': True,
-                })
-            self.assert_(boolean1_id)
-
-            boolean1 = self.boolean.read(boolean1_id, ['boolean'])
-            self.assert_(boolean1['boolean'] == True)
-
-            boolean_ids = self.boolean.search([
-                ('boolean', '=', True),
-                ])
-            self.assert_(boolean_ids == [boolean1_id])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', '!=', True),
-                ])
-            self.assert_(boolean_ids == [])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'in', [True]),
-                ])
-            self.assert_(boolean_ids == [boolean1_id])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'in', [False]),
-                ])
-            self.assert_(boolean_ids == [])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'not in', [True]),
-                ])
-            self.assert_(boolean_ids == [])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'not in', [False]),
-                ])
-            self.assert_(boolean_ids == [boolean1_id])
-
-            boolean2_id = self.boolean.create({
-                'boolean': False,
-                })
-            self.assert_(boolean2_id)
-
-            boolean2 = self.boolean.read(boolean2_id, ['boolean'])
-            self.assert_(boolean2['boolean'] == False)
-
-            boolean_ids = self.boolean.search([
-                ('boolean', '=', False),
-                ])
-            self.assert_(boolean_ids == [boolean2_id])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'in', [True, False]),
-                ])
-            self.assert_(boolean_ids == [boolean1_id, boolean2_id])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'not in', [True, False]),
-                ])
-            self.assert_(boolean_ids == [])
-
-            boolean3_id = self.boolean.create({})
-            self.assert_(boolean3_id)
+            boolean1 = self.boolean.create({
+                    'boolean': True,
+                    })
+            self.assert_(boolean1)
+            self.assertEqual(boolean1.boolean, True)
+
+            booleans = self.boolean.search([
+                    ('boolean', '=', True),
+                    ])
+            self.assertEqual(booleans, [boolean1])
+
+            booleans = self.boolean.search([
+                    ('boolean', '!=', True),
+                    ])
+            self.assertEqual(booleans, [])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'in', [True]),
+                    ])
+            self.assertEqual(booleans, [boolean1])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'in', [False]),
+                    ])
+            self.assertEqual(booleans, [])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'not in', [True]),
+                    ])
+            self.assertEqual(booleans, [])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'not in', [False]),
+                    ])
+            self.assertEqual(booleans, [boolean1])
+
+            boolean2 = self.boolean.create({
+                    'boolean': False,
+                    })
+            self.assert_(boolean2)
+            self.assertEqual(boolean2.boolean, False)
+
+            booleans = self.boolean.search([
+                    ('boolean', '=', False),
+                    ])
+            self.assertEqual(booleans, [boolean2])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'in', [True, False]),
+                    ])
+            self.assertEqual(booleans, [boolean1, boolean2])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'not in', [True, False]),
+                    ])
+            self.assertEqual(booleans, [])
+
+            boolean3 = self.boolean.create({})
+            self.assert_(boolean3)
+            self.assertEqual(boolean3.boolean, False)
 
             # Test search with NULL value
-            boolean4_id = self.boolean.create({
+            boolean4 = self.boolean.create({
                     'boolean': None,
                     })
-            self.assert_(boolean4_id)
-
-            boolean_ids = self.boolean.search([
+            self.assert_(boolean4)
+
+            booleans = self.boolean.search([
                     ('boolean', '=', False),
                     ])
-            self.assertEqual(boolean_ids,
-                [boolean2_id, boolean3_id, boolean4_id])
-
-            boolean_ids = self.boolean.search([
+            self.assertEqual(booleans,
+                [boolean2, boolean3, boolean4])
+
+            booleans = self.boolean.search([
                     ('boolean', '!=', False),
                     ])
-            self.assertEqual(boolean_ids, [boolean1_id])
-
-            boolean3 = self.boolean.read(boolean3_id, ['boolean'])
-            self.assert_(boolean3['boolean'] == False)
-
-            boolean4_id = self.boolean_default.create({})
-            self.assert_(boolean4_id)
-
-            boolean4 = self.boolean_default.read(boolean4_id, ['boolean'])
-            self.assert_(boolean4['boolean'] == True)
-
-            self.boolean.write(boolean1_id, {
-                'boolean': False,
-                })
-            boolean1 = self.boolean.read(boolean1_id, ['boolean'])
-            self.assert_(boolean1['boolean'] == False)
-
-            self.boolean.write(boolean2_id, {
-                'boolean': True,
-                })
-            boolean2 = self.boolean.read(boolean2_id, ['boolean'])
-            self.assert_(boolean2['boolean'] == True)
+            self.assertEqual(booleans, [boolean1])
+
+            boolean4 = self.boolean_default.create({})
+            self.assert_(boolean4)
+            self.assert_(boolean4.boolean == True)
+
+            self.boolean.write([boolean1], {
+                    'boolean': False,
+                    })
+            self.assertEqual(boolean1.boolean, False)
+
+            self.boolean.write([boolean2], {
+                    'boolean': True,
+                    })
+            self.assertEqual(boolean2.boolean, True)
 
             transaction.cursor.rollback()
 
@@ -216,191 +208,179 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            integer1_id = self.integer.create({
-                'integer': 1,
-                })
-            self.assert_(integer1_id)
-
-            integer1 = self.integer.read(integer1_id, ['integer'])
-            self.assert_(integer1['integer'] == 1)
-
-            integer_ids = self.integer.search([
-                ('integer', '=', 1),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '=', 0),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '!=', 1),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '!=', 0),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'in', [1]),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'in', [0]),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', 'in', []),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', 'not in', [1]),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', 'not in', [0]),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'not in', []),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '<', 5),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '<', -5),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '<', 1),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '<=', 5),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '<=', -5),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '<=', 1),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '>', 5),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '>', -5),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '>', 1),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '>=', 5),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '>=', -5),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '>=', 1),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer2_id = self.integer.create({
-                'integer': 0,
-                })
-            self.assert_(integer2_id)
-
-            integer2 = self.integer.read(integer2_id, ['integer'])
-            self.assert_(integer2['integer'] == 0)
-
-            integer_ids = self.integer.search([
-                ('integer', '=', 0),
-                ])
-            self.assert_(integer_ids == [integer2_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'in', [0, 1]),
-                ])
-            self.assert_(integer_ids == [integer1_id, integer2_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'not in', [0, 1]),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer3_id = self.integer.create({})
-            self.assert_(integer3_id)
-
-            integer3 = self.integer.read(integer3_id, ['integer'])
-            self.assert_(integer3['integer'] is None)
-
-            integer4_id = self.integer_default.create({})
-            self.assert_(integer4_id)
-
-            integer4 = self.integer_default.read(integer4_id, ['integer'])
-            self.assert_(integer4['integer'] == 5)
-
-            self.integer.write(integer1_id, {
-                'integer': 0,
-                })
-            integer1 = self.integer.read(integer1_id, ['integer'])
-            self.assert_(integer1['integer'] == 0)
-
-            self.integer.write(integer2_id, {
-                'integer': 1,
-                })
-            integer2 = self.integer.read(integer2_id, ['integer'])
-            self.assert_(integer2['integer'] == 1)
-
-            self.failUnlessRaises(Exception, self.integer.create, {
-                'integer': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.integer.write, integer1_id, {
-                'integer': 'test',
-                })
+            integer1 = self.integer.create({
+                    'integer': 1,
+                    })
+            self.assert_(integer1)
+            self.assertEqual(integer1.integer, 1)
+
+            integers = self.integer.search([
+                    ('integer', '=', 1),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '=', 0),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '!=', 1),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '!=', 0),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', 'in', [1]),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', 'in', [0]),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', 'in', []),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', 'not in', [1]),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', 'not in', [0]),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', 'not in', []),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '<', 5),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '<', -5),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '<', 1),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '<=', 5),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '<=', -5),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '<=', 1),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '>', 5),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '>', -5),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '>', 1),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '>=', 5),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '>=', -5),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '>=', 1),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integer2 = self.integer.create({
+                    'integer': 0,
+                    })
+            self.assert_(integer2)
+            self.assertEqual(integer2.integer, 0)
+
+            integers = self.integer.search([
+                    ('integer', '=', 0),
+                    ])
+            self.assertEqual(integers, [integer2])
+
+            integers = self.integer.search([
+                    ('integer', 'in', [0, 1]),
+                    ])
+            self.assertEqual(integers, [integer1, integer2])
+
+            integers = self.integer.search([
+                    ('integer', 'not in', [0, 1]),
+                    ])
+            self.assertEqual(integers, [])
+
+            integer3 = self.integer.create({})
+            self.assert_(integer3)
+            self.assertEqual(integer3.integer, None)
+
+            integer4 = self.integer_default.create({})
+            self.assert_(integer4)
+            self.assertEqual(integer4.integer, 5)
+
+            self.integer.write([integer1], {
+                    'integer': 0,
+                    })
+            self.assertEqual(integer1.integer, 0)
+
+            self.integer.write([integer2], {
+                    'integer': 1,
+                    })
+            self.assertEqual(integer2.integer, 1)
+
+            self.assertRaises(Exception, self.integer.create, {
+                    'integer': 'test',
+                    })
+
+            self.assertRaises(Exception, self.integer.write, [integer1], {
+                    'integer': 'test',
+                    })
 
             # We should catch UserError but mysql does not raise an
             # IntegrityError but an OperationalError
             self.assertRaises(Exception, self.integer_required.create, {})
             transaction.cursor.rollback()
 
-            integer5_id = self.integer_required.create({
+            integer5 = self.integer_required.create({
                     'integer': 0,
                     })
-            self.assert_(integer5_id)
-
-            integer5 = self.integer_required.read(integer5_id, ['integer'])
-            self.assert_(integer5['integer'] == 0)
+            self.assert_(integer5)
+            self.assertEqual(integer5.integer, 0)
 
             transaction.cursor.rollback()
 
@@ -410,215 +390,202 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            float1_id = self.float.create({
-                'float': 1.1,
-                })
-            self.assert_(float1_id)
-
-            float1 = self.float.read(float1_id, ['float'])
-            self.assert_(float1['float'] == 1.1)
-
-            float_ids = self.float.search([
-                ('float', '=', 1.1),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '=', 0),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '!=', 1.1),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '!=', 0),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', 'in', [1.1]),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', 'in', [0]),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', 'in', []),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', 'not in', [1.1]),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', 'not in', [0]),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', 'not in', []),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '<', 5),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '<', -5),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '<', 1.1),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '<=', 5),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '<=', -5),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '<=', 1.1),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '>', 5),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '>', -5),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '>', 1.1),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '>=', 5),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '>=', -5),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '>=', 1.1),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float2_id = self.float.create({
-                'float': 0,
-                })
-            self.assert_(float2_id)
-
-            float2 = self.float.read(float2_id, ['float'])
-            self.assert_(float2['float'] == 0)
-
-            float_ids = self.float.search([
-                ('float', '=', 0),
-                ])
-            self.assert_(float_ids == [float2_id])
-
-            float_ids = self.float.search([
-                ('float', 'in', [0, 1.1]),
-                ])
-            self.assert_(float_ids == [float1_id, float2_id])
-
-            float_ids = self.float.search([
-                ('float', 'not in', [0, 1.1]),
-                ])
-            self.assert_(float_ids == [])
-
-            float3_id = self.float.create({})
-            self.assert_(float3_id)
-
-            float3 = self.float.read(float3_id, ['float'])
-            self.assert_(float3['float'] is None)
-
-            float4_id = self.float_default.create({})
-            self.assert_(float4_id)
-
-            float4 = self.float_default.read(float4_id, ['float'])
-            self.assert_(float4['float'] == 5.5)
-
-            self.float.write(float1_id, {
-                'float': 0,
-                })
-            float1 = self.float.read(float1_id, ['float'])
-            self.assert_(float1['float'] == 0)
-
-            self.float.write(float2_id, {
-                'float': 1.1,
-                })
-            float2 = self.float.read(float2_id, ['float'])
-            self.assert_(float2['float'] == 1.1)
-
-            self.failUnlessRaises(Exception, self.float.create, {
-                'float': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.float.write, float1_id, {
-                'float': 'test',
-                })
+            float1 = self.float.create({
+                    'float': 1.1,
+                    })
+            self.assert_(float1)
+            self.assertEqual(float1.float, 1.1)
+
+            floats = self.float.search([
+                    ('float', '=', 1.1),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '=', 0),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '!=', 1.1),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '!=', 0),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', 'in', [1.1]),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', 'in', [0]),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', 'in', []),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', 'not in', [1.1]),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', 'not in', [0]),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', 'not in', []),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '<', 5),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '<', -5),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '<', 1.1),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '<=', 5),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '<=', -5),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '<=', 1.1),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '>', 5),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '>', -5),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '>', 1.1),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '>=', 5),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '>=', -5),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '>=', 1.1),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            float2 = self.float.create({
+                    'float': 0,
+                    })
+            self.assert_(float2)
+            self.assertEqual(float2.float, 0)
+
+            floats = self.float.search([
+                    ('float', '=', 0),
+                    ])
+            self.assertEqual(floats, [float2])
+
+            floats = self.float.search([
+                    ('float', 'in', [0, 1.1]),
+                    ])
+            self.assertEqual(floats, [float1, float2])
+
+            floats = self.float.search([
+                    ('float', 'not in', [0, 1.1]),
+                    ])
+            self.assertEqual(floats, [])
+
+            float3 = self.float.create({})
+            self.assert_(float3)
+            self.assertEqual(float3.float, None)
+
+            float4 = self.float_default.create({})
+            self.assert_(float4)
+            self.assertEqual(float4.float, 5.5)
+
+            self.float.write([float1], {
+                    'float': 0,
+                    })
+            self.assertEqual(float1.float, 0)
+
+            self.float.write([float2], {
+                    'float': 1.1,
+                    })
+            self.assertEqual(float2.float, 1.1)
+
+            self.assertRaises(Exception, self.float.create, {
+                    'float': 'test',
+                    })
+
+            self.assertRaises(Exception, self.float.write, [float1], {
+                    'float': 'test',
+                    })
 
             self.assertRaises(Exception, self.float_required.create, {})
             transaction.cursor.rollback()
 
-            float5_id = self.float_required.create({
+            float5 = self.float_required.create({
                     'float': 0.0,
                     })
-            float5 = self.float_required.read(float5_id)
-            self.assert_(float5['float'] == 0.0)
-
-            float6_id = self.float_digits.create({
-                'digits': 1,
-                'float': 1.1,
-                })
-            self.assert_(float6_id)
-
-            self.failUnlessRaises(Exception, self.float_digits.create, {
-                'digits': 1,
-                'float': 1.11,
-                })
-
-            self.failUnlessRaises(Exception, self.float_digits.write,
-                float6_id, {
+            self.assertEqual(float5.float, 0.0)
+
+            float6 = self.float_digits.create({
+                    'digits': 1,
+                    'float': 1.1,
+                    })
+            self.assert_(float6)
+
+            self.assertRaises(Exception, self.float_digits.create, {
+                    'digits': 1,
                     'float': 1.11,
                     })
 
-            self.failUnlessRaises(Exception, self.float_digits.write,
-                float6_id, {
+            self.assertRaises(Exception, self.float_digits.write,
+                [float6], {
+                    'float': 1.11,
+                    })
+
+            self.assertRaises(Exception, self.float_digits.write,
+                [float6], {
                     'digits': 0,
                     })
 
-            float7_id = self.float.create({
-                'float': 0.123456789012345,
-                })
-
-            float7 = self.float.read(float7_id, ['float'])
-            self.assert_(float7['float'] == 0.123456789012345)
+            float7 = self.float.create({
+                    'float': 0.123456789012345,
+                    })
+            self.assertEqual(float7.float, 0.123456789012345)
 
             transaction.cursor.rollback()
 
@@ -628,221 +595,208 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            numeric1_id = self.numeric.create({
-                'numeric': Decimal('1.1'),
-                })
-            self.assert_(numeric1_id)
-
-            numeric1 = self.numeric.read(numeric1_id, ['numeric'])
-            self.assert_(numeric1['numeric'] == Decimal('1.1'))
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '=', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '=', Decimal('0')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '!=', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '!=', Decimal('0')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'in', [Decimal('1.1')]),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'in', [Decimal('0')]),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'in', []),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'not in', [Decimal('1.1')]),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'not in', [Decimal('0')]),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'not in', []),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<', Decimal('5')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<', Decimal('-5')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<=', Decimal('5')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<=', Decimal('-5')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<=', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>', Decimal('5')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>', Decimal('-5')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>=', Decimal('5')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>=', Decimal('-5')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>=', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric2_id = self.numeric.create({
-                'numeric': Decimal('0'),
-                })
-            self.assert_(numeric2_id)
-
-            numeric2 = self.numeric.read(numeric2_id, ['numeric'])
-            self.assert_(numeric2['numeric'] == Decimal('0'))
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '=', Decimal('0')),
-                ])
-            self.assert_(numeric_ids == [numeric2_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'in', [Decimal('0'), Decimal('1.1')]),
-                ])
-            self.assert_(numeric_ids == [numeric1_id, numeric2_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'not in', [Decimal('0'), Decimal('1.1')]),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric3_id = self.numeric.create({})
-            self.assert_(numeric3_id)
-
-            numeric3 = self.numeric.read(numeric3_id, ['numeric'])
-            self.assert_(numeric3['numeric'] is None)
-
-            numeric4_id = self.numeric_default.create({})
-            self.assert_(numeric4_id)
-
-            numeric4 = self.numeric_default.read(numeric4_id, ['numeric'])
-            self.assert_(numeric4['numeric'] == Decimal('5.5'))
-
-            self.numeric.write(numeric1_id, {
-                'numeric': Decimal('0'),
-                })
-            numeric1 = self.numeric.read(numeric1_id, ['numeric'])
-            self.assert_(numeric1['numeric'] == Decimal('0'))
-
-            self.numeric.write(numeric2_id, {
-                'numeric': Decimal('1.1'),
-                })
-            numeric2 = self.numeric.read(numeric2_id, ['numeric'])
-            self.assert_(numeric2['numeric'] == Decimal('1.1'))
-
-            self.failUnlessRaises(Exception, self.numeric.create, {
-                'numeric': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.numeric.write, numeric1_id, {
-                'numeric': 'test',
-                })
+            numeric1 = self.numeric.create({
+                    'numeric': Decimal('1.1'),
+                    })
+            self.assert_(numeric1)
+            self.assertEqual(numeric1.numeric, Decimal('1.1'))
+
+            numerics = self.numeric.search([
+                    ('numeric', '=', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '=', Decimal('0')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '!=', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '!=', Decimal('0')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'in', [Decimal('1.1')]),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'in', [Decimal('0')]),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'in', []),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'not in', [Decimal('1.1')]),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'not in', [Decimal('0')]),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'not in', []),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<', Decimal('-5')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<=', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<=', Decimal('-5')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<=', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>', Decimal('-5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>=', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>=', Decimal('-5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>=', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numeric2 = self.numeric.create({
+                    'numeric': Decimal('0'),
+                    })
+            self.assert_(numeric2)
+            self.assertEqual(numeric2.numeric, Decimal('0'))
+
+            numerics = self.numeric.search([
+                    ('numeric', '=', Decimal('0')),
+                    ])
+            self.assertEqual(numerics, [numeric2])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'in', [Decimal('0'), Decimal('1.1')]),
+                    ])
+            self.assertEqual(numerics, [numeric1, numeric2])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'not in', [Decimal('0'), Decimal('1.1')]),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numeric3 = self.numeric.create({})
+            self.assert_(numeric3)
+            self.assertEqual(numeric3.numeric, None)
+
+            numeric4 = self.numeric_default.create({})
+            self.assert_(numeric4)
+            self.assertEqual(numeric4.numeric, Decimal('5.5'))
+
+            self.numeric.write([numeric1], {
+                    'numeric': Decimal('0'),
+                    })
+            self.assertEqual(numeric1.numeric, Decimal('0'))
+
+            self.numeric.write([numeric2], {
+                    'numeric': Decimal('1.1'),
+                    })
+            self.assertEqual(numeric2.numeric, Decimal('1.1'))
+
+            self.assertRaises(Exception, self.numeric.create, {
+                    'numeric': 'test',
+                    })
+
+            self.assertRaises(Exception, self.numeric.write, [numeric1], {
+                    'numeric': 'test',
+                    })
 
             self.assertRaises(Exception, self.numeric_required.create, {})
             transaction.cursor.rollback()
 
-            numeric5_id = self.numeric_required.create({
+            numeric5 = self.numeric_required.create({
                     'numeric': Decimal(0),
                     })
-            numeric5 = self.numeric_required.read(numeric5_id)
-            self.assert_(numeric5['numeric'] == 0)
-
-            numeric6_id = self.numeric_digits.create({
-                'digits': 1,
-                'numeric': Decimal('1.1'),
-                })
-            self.assert_(numeric6_id)
-
-            self.failUnlessRaises(Exception, self.numeric_digits.create, {
-                'digits': 1,
-                'numeric': Decimal('1.11'),
-                })
-
-            self.failUnlessRaises(Exception, self.numeric_digits.write,
-                    numeric6_id, {
-                        'numeric': Decimal('1.11'),
-                        })
-
-            self.failUnlessRaises(Exception, self.numeric_digits.write,
-                    numeric6_id, {
-                        'numeric': Decimal('0.10000000000000001'),
-                        })
-
-            self.failUnlessRaises(Exception, self.numeric_digits.write,
-                    numeric6_id, {
-                        'digits': 0,
-                        })
-
-            numeric7_id = self.numeric.create({
-                'numeric': Decimal('0.1234567890123456789'),
-                })
-
-            numeric7 = self.numeric.read(numeric7_id, ['numeric'])
-            self.assert_(numeric7['numeric'] ==
-                    Decimal('0.1234567890123456789'))
+            self.assertEqual(numeric5.numeric, 0)
+
+            numeric6 = self.numeric_digits.create({
+                    'digits': 1,
+                    'numeric': Decimal('1.1'),
+                    })
+            self.assert_(numeric6)
+
+            self.assertRaises(Exception, self.numeric_digits.create, {
+                    'digits': 1,
+                    'numeric': Decimal('1.11'),
+                    })
+
+            self.assertRaises(Exception, self.numeric_digits.write,
+                [numeric6], {
+                    'numeric': Decimal('1.11'),
+                    })
+
+            self.assertRaises(Exception, self.numeric_digits.write,
+                [numeric6], {
+                    'numeric': Decimal('0.10000000000000001'),
+                    })
+
+            self.assertRaises(Exception, self.numeric_digits.write,
+                [numeric6], {
+                    'digits': 0,
+                    })
+
+            numeric7 = self.numeric.create({
+                    'numeric': Decimal('0.1234567890123456789'),
+                    })
+            self.assertEqual(numeric7.numeric,
+                Decimal('0.1234567890123456789'))
 
             transaction.cursor.rollback()
 
@@ -852,262 +806,249 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            for char in (self.char, self.char_translate):
-                char1_id = char.create({
+            for char in (self.char_translate, self.char):
+                char1 = char.create({
+                        'char': 'Test',
+                        })
+                self.assert_(char1)
+                self.assertEqual(char1.char, 'Test')
+
+                chars = char.search([
+                        ('char', '=', 'Test'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', '=', 'Foo'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', '=', None),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', '!=', 'Test'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', '!=', 'Foo'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', '!=', None),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'in', ['Test']),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'in', ['Foo']),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'in', [None]),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'in', []),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not in', ['Test']),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not in', ['Foo']),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not in', [None]),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not in', []),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'like', 'Test'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'like', 'T%'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'like', 'Foo'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'like', 'F%'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'ilike', 'test'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'ilike', 't%'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'ilike', 'foo'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'ilike', 'f%'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not like', 'Test'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not like', 'T%'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not like', 'Foo'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not like', 'F%'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not ilike', 'test'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not ilike', 't%'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not ilike', 'foo'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not ilike', 'f%'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                char2 = char.create({
+                        'char': None,
+                        })
+                self.assert_(char2)
+                self.assertEqual(char2.char, None)
+
+                chars = char.search([
+                        ('char', '=', None),
+                        ])
+                self.assertEqual(chars, [char2])
+
+                chars = char.search([
+                        ('char', 'in', [None, 'Test']),
+                        ])
+                self.assertEqual(chars, [char1, char2])
+
+                chars = char.search([
+                        ('char', 'not in', [None, 'Test']),
+                        ])
+                self.assertEqual(chars, [])
+
+            char3 = self.char.create({})
+            self.assert_(char3)
+            self.assertEqual(char3.char, None)
+
+            char4 = self.char_default.create({})
+            self.assert_(char4)
+            self.assertEqual(char4.char, 'Test')
+
+            self.char.write([char1], {
+                    'char': None,
+                    })
+            self.assertEqual(char1.char, None)
+
+            self.char.write([char2], {
                     'char': 'Test',
                     })
-                self.assert_(char1_id)
-
-                char1 = char.read(char1_id, ['char'])
-                self.assert_(char1['char'] == 'Test')
-
-                char_ids = char.search([
-                    ('char', '=', 'Test'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', '=', 'Foo'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', '=', False),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', '!=', 'Test'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', '!=', 'Foo'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', '!=', False),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'in', ['Test']),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'in', ['Foo']),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'in', [False]),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'in', []),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not in', ['Test']),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not in', ['Foo']),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not in', [False]),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not in', []),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'like', 'Test'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'like', 'T%'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'like', 'Foo'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'like', 'F%'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'ilike', 'test'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'ilike', 't%'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'ilike', 'foo'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'ilike', 'f%'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not like', 'Test'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not like', 'T%'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not like', 'Foo'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not like', 'F%'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not ilike', 'test'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not ilike', 't%'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not ilike', 'foo'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not ilike', 'f%'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char2_id = char.create({
-                    'char': None,
-                    })
-                self.assert_(char2_id)
-
-                char2 = char.read(char2_id, ['char'])
-                self.assert_(char2['char'] == None)
-
-                char_ids = char.search([
-                    ('char', '=', False),
-                    ])
-                self.assert_(char_ids == [char2_id])
-
-                char_ids = char.search([
-                    ('char', 'in', [False, 'Test']),
-                    ])
-                self.assert_(char_ids == [char1_id, char2_id])
-
-                char_ids = char.search([
-                    ('char', 'not in', [False, 'Test']),
-                    ])
-                self.assert_(char_ids == [])
-
-            char3_id = self.char.create({})
-            self.assert_(char3_id)
-
-            char3 = self.char.read(char3_id, ['char'])
-            self.assert_(char3['char'] == None)
-
-            char4_id = self.char_default.create({})
-            self.assert_(char4_id)
-
-            char4 = self.char_default.read(char4_id, ['char'])
-            self.assert_(char4['char'] == 'Test')
-
-            self.char.write(char1_id, {
-                'char': None,
-                })
-            char1 = self.char.read(char1_id, ['char'])
-            self.assert_(char1['char'] == None)
-
-            self.char.write(char2_id, {
-                'char': 'Test',
-                })
-            char2 = self.char.read(char2_id, ['char'])
-            self.assert_(char2['char'] == 'Test')
-
-            self.failUnlessRaises(Exception, self.char_required.create, {})
+            self.assertEqual(char2.char, 'Test')
+
+            self.assertRaises(Exception, self.char_required.create, {})
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.char_required.create, {
+            self.assertRaises(Exception, self.char_required.create, {
                     'char': '',
                     })
             transaction.cursor.rollback()
 
-            char5_id = self.char_required.create({
-                'char': 'Test',
-                })
-            self.assert_(char5_id)
-
-            char6_id = self.char_size.create({
-                'char': 'Test',
-                })
-            self.assert_(char6_id)
-
-            self.failUnlessRaises(Exception, self.char_size.create, {
-                'char': 'foobar',
-                })
-
-            self.failUnlessRaises(Exception, self.char_size.write, char6_id, {
-                'char': 'foobar',
-                })
+            char5 = self.char_required.create({
+                    'char': 'Test',
+                    })
+            self.assert_(char5)
+
+            char6 = self.char_size.create({
+                    'char': 'Test',
+                    })
+            self.assert_(char6)
+
+            self.assertRaises(Exception, self.char_size.create, {
+                    'char': 'foobar',
+                    })
+
+            self.assertRaises(Exception, self.char_size.write, [char6], {
+                    'char': 'foobar',
+                    })
             transaction.cursor.rollback()
 
-            char7_id = self.char.create({
-                'char': u'é',
-                })
-            self.assert_(char7_id)
-
-            char7 = self.char.read(char7_id, ['char'])
-            self.assert_(char7['char'] == u'é')
-
-            char_ids = self.char.search([
-                ('char', '=', u'é'),
-                ])
-            self.assert_(char_ids == [char7_id])
-
-            self.char.write(char7_id, {
-                'char': 'é',
-                })
-            char7 = self.char.read(char7_id, ['char'])
-            self.assert_(char7['char'] == u'é')
-
-            char_ids = self.char.search([
-                ('char', '=', 'é'),
-                ])
-            self.assert_(char_ids == [char7_id])
+            char7 = self.char.create({
+                    'char': u'é',
+                    })
+            self.assert_(char7)
+            self.assertEqual(char7.char, u'é')
+
+            chars = self.char.search([
+                    ('char', '=', u'é'),
+                    ])
+            self.assertEqual(chars, [char7])
+
+            self.char.write([char7], {
+                    'char': 'é',
+                    })
+            self.assertEqual(char7.char, u'é')
+
+            chars = self.char.search([
+                    ('char', '=', 'é'),
+                    ])
+            self.assertEqual(chars, [char7])
 
             transaction.cursor.rollback()
 
@@ -1117,261 +1058,248 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            for text in (self.text, self.text_translate):
-                text1_id = text.create({
+            for text in (self.text_translate, self.text):
+                text1 = text.create({
+                        'text': 'Test',
+                        })
+                self.assert_(text1)
+                self.assertEqual(text1.text, 'Test')
+
+                texts = text.search([
+                        ('text', '=', 'Test'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', '=', 'Foo'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', '=', None),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', '!=', 'Test'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', '!=', 'Foo'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', '!=', None),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'in', ['Test']),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'in', ['Foo']),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'in', [None]),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'in', []),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not in', ['Test']),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not in', ['Foo']),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not in', [None]),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not in', []),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'like', 'Test'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'like', 'T%'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'like', 'Foo'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'like', 'F%'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'ilike', 'test'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'ilike', 't%'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'ilike', 'foo'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'ilike', 'f%'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not like', 'Test'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not like', 'T%'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not like', 'Foo'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not like', 'F%'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not ilike', 'test'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not ilike', 't%'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not ilike', 'foo'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not ilike', 'f%'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                text2 = text.create({
+                        'text': None,
+                        })
+                self.assert_(text2)
+                self.assertEqual(text2.text, None)
+
+                texts = text.search([
+                        ('text', '=', None),
+                        ])
+                self.assertEqual(texts, [text2])
+
+                texts = text.search([
+                        ('text', 'in', [None, 'Test']),
+                        ])
+                self.assertEqual(texts, [text1, text2])
+
+                texts = text.search([
+                        ('text', 'not in', [None, 'Test']),
+                        ])
+                self.assertEqual(texts, [])
+
+            text3 = self.text.create({})
+            self.assert_(text3)
+            self.assertEqual(text3.text, None)
+
+            text4 = self.text_default.create({})
+            self.assert_(text4)
+            self.assertEqual(text4.text, 'Test')
+
+            self.text.write([text1], {
+                    'text': None,
+                    })
+            self.assertEqual(text1.text, None)
+
+            self.text.write([text2], {
                     'text': 'Test',
                     })
-                self.assert_(text1_id)
-
-                text1 = text.read(text1_id, ['text'])
-                self.assert_(text1['text'] == 'Test')
-
-                text_ids = text.search([
-                    ('text', '=', 'Test'),
+            self.assertEqual(text2.text, 'Test')
+
+            self.assertRaises(Exception, self.text_required.create, {})
+            transaction.cursor.rollback()
+
+            text5 = self.text_required.create({
+                    'text': 'Test',
+                    })
+            self.assert_(text5)
+
+            text6 = self.text_size.create({
+                    'text': 'Test',
+                    })
+            self.assert_(text6)
+
+            self.assertRaises(Exception, self.text_size.create, {
+                    'text': 'foobar',
+                    })
+
+            self.assertRaises(Exception, self.text_size.write, [text6], {
+                    'text': 'foobar',
+                    })
+
+            text7 = self.text.create({
+                    'text': 'Foo\nBar',
+                    })
+            self.assert_(text7)
+
+            text8 = self.text.create({
+                    'text': u'é',
+                    })
+            self.assert_(text8)
+            self.assertEqual(text8.text, u'é')
+
+            texts = self.text.search([
+                    ('text', '=', u'é'),
                     ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', '=', 'Foo'),
+            self.assertEqual(texts, [text8])
+
+            self.text.write([text8], {
+                    'text': 'é',
+                    })
+            self.assertEqual(text8.text, u'é')
+
+            texts = self.text.search([
+                    ('text', '=', 'é'),
                     ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', '=', False),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', '!=', 'Test'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', '!=', 'Foo'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', '!=', False),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'in', ['Test']),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'in', ['Foo']),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'in', [False]),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'in', []),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not in', ['Test']),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not in', ['Foo']),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not in', [False]),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not in', []),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'like', 'Test'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'like', 'T%'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'like', 'Foo'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'like', 'F%'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'ilike', 'test'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'ilike', 't%'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'ilike', 'foo'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'ilike', 'f%'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not like', 'Test'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not like', 'T%'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not like', 'Foo'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not like', 'F%'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not ilike', 'test'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not ilike', 't%'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not ilike', 'foo'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not ilike', 'f%'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text2_id = text.create({
-                    'text': None,
-                    })
-                self.assert_(text2_id)
-
-                text2 = text.read(text2_id, ['text'])
-                self.assert_(text2['text'] == None)
-
-                text_ids = text.search([
-                    ('text', '=', False),
-                    ])
-                self.assert_(text_ids == [text2_id])
-
-                text_ids = text.search([
-                    ('text', 'in', [False, 'Test']),
-                    ])
-                self.assert_(text_ids == [text1_id, text2_id])
-
-                text_ids = text.search([
-                    ('text', 'not in', [False, 'Test']),
-                    ])
-                self.assert_(text_ids == [])
-
-            text3_id = self.text.create({})
-            self.assert_(text3_id)
-
-            text3 = self.text.read(text3_id, ['text'])
-            self.assert_(text3['text'] == None)
-
-            text4_id = self.text_default.create({})
-            self.assert_(text4_id)
-
-            text4 = self.text_default.read(text4_id, ['text'])
-            self.assert_(text4['text'] == 'Test')
-
-            self.text.write(text1_id, {
-                'text': None,
-                })
-            text1 = self.text.read(text1_id, ['text'])
-            self.assert_(text1['text'] == None)
-
-            self.text.write(text2_id, {
-                'text': 'Test',
-                })
-            text2 = self.text.read(text2_id, ['text'])
-            self.assert_(text2['text'] == 'Test')
-
-            self.failUnlessRaises(Exception, self.text_required.create, {})
-            transaction.cursor.rollback()
-
-            text5_id = self.text_required.create({
-                'text': 'Test',
-                })
-            self.assert_(text5_id)
-
-            text6_id = self.text_size.create({
-                'text': 'Test',
-                })
-            self.assert_(text6_id)
-
-            self.failUnlessRaises(Exception, self.text_size.create, {
-                'text': 'foobar',
-                })
-
-            self.failUnlessRaises(Exception, self.text_size.write, text6_id, {
-                'text': 'foobar',
-                })
-
-            text7_id = self.text.create({
-                'text': 'Foo\nBar',
-                })
-            self.assert_(text7_id)
-
-            text8_id = self.text.create({
-                'text': u'é',
-                })
-            self.assert_(text8_id)
-
-            text8 = self.text.read(text8_id, ['text'])
-            self.assert_(text8['text'] == u'é')
-
-            text_ids = self.text.search([
-                ('text', '=', u'é'),
-                ])
-            self.assert_(text_ids == [text8_id])
-
-            self.text.write(text8_id, {
-                'text': 'é',
-                })
-            text8 = self.text.read(text8_id, ['text'])
-            self.assert_(text8['text'] == u'é')
-
-            text_ids = self.text.search([
-                ('text', '=', 'é'),
-                ])
-            self.assert_(text_ids == [text8_id])
+            self.assertEqual(texts, [text8])
 
             transaction.cursor.rollback()
 
@@ -1381,202 +1309,189 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            sha1_id = self.sha.create({
-                'sha': 'Test',
-                })
-            self.assert_(sha1_id)
-
-            sha1 = self.sha.read(sha1_id, ['sha'])
-            self.assertEqual(sha1['sha'],
+            sha1 = self.sha.create({
+                    'sha': 'Test',
+                    })
+            self.assert_(sha1)
+            self.assertEqual(sha1.sha,
                 '640ab2bae07bedc4c163f679a746f7ab7fb5d1fa')
 
-            sha_ids = self.sha.search([
-                ('sha', '=', 'Test'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', '=', 'Foo'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', '=', False),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', '!=', 'Test'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', '!=', 'Foo'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', '!=', False),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', ['Test']),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', ['Foo']),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', [False]),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', []),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', ['Test']),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', ['Foo']),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', [False]),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', []),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'like', 'Test'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'like', 'Foo'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'ilike', 'Test'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'ilike', 'foo'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not like', 'Test'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not like', 'Foo'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not ilike', 'foo'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha2_id = self.sha.create({
-                'sha': None,
-                })
-            self.assert_(sha2_id)
-
-            sha2 = self.sha.read(sha2_id, ['sha'])
-            self.assert_(sha2['sha'] == None)
-
-            sha_ids = self.sha.search([
-                ('sha', '=', False),
-                ])
-            self.assert_(sha_ids == [sha2_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', [False, 'Test']),
-                ])
-            self.assert_(sha_ids == [sha1_id, sha2_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', [False, 'Test']),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha3_id = self.sha.create({})
-            self.assert_(sha3_id)
-
-            sha3 = self.sha.read(sha3_id, ['sha'])
-            self.assert_(sha3['sha'] == None)
-
-            sha4_id = self.sha_default.create({})
-            self.assert_(sha4_id)
-
-            sha4 = self.sha_default.read(sha4_id, ['sha'])
-            self.assertEqual(sha4['sha'],
+            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_id, {
-                'sha': None,
-                })
-            sha1 = self.sha.read(sha1_id, ['sha'])
-            self.assert_(sha1['sha'] == None)
-
-            self.sha.write(sha2_id, {
-                'sha': 'Test',
-                })
-            sha2 = self.sha.read(sha2_id, ['sha'])
-            self.assertEqual(sha2['sha'],
+            self.sha.write([sha1], {
+                    'sha': None,
+                    })
+            self.assertEqual(sha1.sha, None)
+
+            self.sha.write([sha2], {
+                    'sha': 'Test',
+                    })
+            self.assertEqual(sha2.sha,
                 '640ab2bae07bedc4c163f679a746f7ab7fb5d1fa')
 
-            self.failUnlessRaises(Exception, self.sha_required.create, {})
+            self.assertRaises(Exception, self.sha_required.create, {})
             transaction.cursor.rollback()
 
-            sha5_id = self.sha_required.create({
-                'sha': 'Test',
-                })
-            self.assert_(sha5_id)
-
-            sha6_id = self.sha.create({
-                'sha': u'é',
-                })
-            self.assert_(sha6_id)
-
-            sha6 = self.sha.read(sha6_id, ['sha'])
-            self.assert_(sha6['sha'] ==
-                    u'bf15be717ac1b080b4f1c456692825891ff5073d')
-
-            sha_ids = self.sha.search([
-                ('sha', '=', u'é'),
-                ])
-            self.assert_(sha_ids == [sha6_id])
-
-            self.sha.write(sha6_id, {
-                'sha': 'é',
-                })
-            sha6 = self.sha.read(sha6_id, ['sha'])
-            self.assert_(sha6['sha'] ==
-                    u'bf15be717ac1b080b4f1c456692825891ff5073d')
-
-            sha_ids = self.sha.search([
-                ('sha', '=', 'é'),
-                ])
-            self.assert_(sha_ids == [sha6_id])
+            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()
 
@@ -1591,247 +1506,236 @@
             yesterday = today - datetime.timedelta(1)
             default_date = datetime.date(2000, 1, 1)
 
-            date1_id = self.date.create({
-                'date': today,
-                })
-            self.assert_(date1_id)
-
-            date1 = self.date.read(date1_id, ['date'])
-            self.assert_(date1['date'] == today)
-
-            date_ids = self.date.search([
-                ('date', '=', today),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '=', tomorrow),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '=', False),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '!=', today),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '!=', tomorrow),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '!=', False),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', 'in', [today]),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', 'in', [tomorrow]),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', 'in', [False]),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', 'in', []),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', 'not in', [today]),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', 'not in', [tomorrow]),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', 'not in', [False]),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', 'not in', []),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '<', tomorrow),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '<', yesterday),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '<', today),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '<=', today),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '<=', yesterday),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '<=', tomorrow),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '>', tomorrow),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '>', yesterday),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '>', today),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '>=', tomorrow),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '>=', yesterday),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '>=', today),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date2_id = self.date.create({
-                'date': yesterday,
-                })
-            self.assert_(date2_id)
-
-            date2 = self.date.read(date2_id, ['date'])
-            self.assert_(date2['date'] == yesterday)
-
-            date_ids = self.date.search([
-                ('date', '=', yesterday),
-                ])
-            self.assert_(date_ids == [date2_id])
-
-            date_ids = self.date.search([
-                ('date', 'in', [yesterday, today]),
-                ])
-            self.assert_(date_ids == [date1_id, date2_id])
-
-            date_ids = self.date.search([
-                ('date', 'not in', [yesterday, today]),
-                ])
-            self.assert_(date_ids == [])
-
-            date3_id = self.date.create({})
-            self.assert_(date3_id)
-
-            date3 = self.date.read(date3_id, ['date'])
-            self.assert_(date3['date'] == None)
-
-            date4_id = self.date_default.create({})
-            self.assert_(date4_id)
-
-            date4 = self.date_default.read(date4_id, ['date'])
-            self.assert_(date4['date'] == default_date)
-
-            self.date.write(date1_id, {
-                'date': yesterday,
-                })
-            date1 = self.date.read(date1_id, ['date'])
-            self.assert_(date1['date'] == yesterday)
-
-            self.date.write(date2_id, {
-                'date': today,
-                })
-            date2 = self.date.read(date2_id, ['date'])
-            self.assert_(date2['date'] == today)
-
-            self.failUnlessRaises(Exception, self.date.create, {
-                'date': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.date.write, date1_id, {
-                'date': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.date.create, {
-                'date': 1,
-                })
-
-            self.failUnlessRaises(Exception, self.date.write, date1_id, {
-                'date': 1,
-                })
-
-            self.failUnlessRaises(Exception, self.date.create, {
-                'date': datetime.datetime.now(),
-                })
-
-            self.failUnlessRaises(Exception, self.date.write, date1_id, {
-                'date': datetime.datetime.now(),
-                })
-
-            self.failUnlessRaises(Exception, self.date.create, {
-                'date': '2009-13-01',
-                })
-
-            self.failUnlessRaises(Exception, self.date.write, date1_id, {
-                'date': '2009-02-29',
-                })
-
-            date5_id = self.date.create({
-                'date': '2009-01-01',
-                })
-            self.assert_(date5_id)
-            date5 = self.date.read(date5_id, ['date'])
-            self.assert_(date5['date'] == datetime.date(2009, 1, 1))
-
-            self.failUnlessRaises(Exception, self.date_required.create, {})
+            date1 = self.date.create({
+                    'date': today,
+                    })
+            self.assert_(date1)
+            self.assertEqual(date1.date, today)
+
+            dates = self.date.search([
+                    ('date', '=', today),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '=', tomorrow),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '=', None),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '!=', today),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '!=', tomorrow),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '!=', None),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', 'in', [today]),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', 'in', [tomorrow]),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', 'in', [None]),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', 'in', []),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', 'not in', [today]),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', 'not in', [tomorrow]),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', 'not in', [None]),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', 'not in', []),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '<', tomorrow),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '<', yesterday),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '<', today),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '<=', today),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '<=', yesterday),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '<=', tomorrow),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '>', tomorrow),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '>', yesterday),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '>', today),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '>=', tomorrow),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '>=', yesterday),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '>=', today),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            date2 = self.date.create({
+                    'date': yesterday,
+                    })
+            self.assert_(date2)
+            self.assertEqual(date2.date, yesterday)
+
+            dates = self.date.search([
+                    ('date', '=', yesterday),
+                    ])
+            self.assertEqual(dates, [date2])
+
+            dates = self.date.search([
+                    ('date', 'in', [yesterday, today]),
+                    ])
+            self.assertEqual(dates, [date1, date2])
+
+            dates = self.date.search([
+                    ('date', 'not in', [yesterday, today]),
+                    ])
+            self.assertEqual(dates, [])
+
+            date3 = self.date.create({})
+            self.assert_(date3)
+            self.assertEqual(date3.date, None)
+
+            date4 = self.date_default.create({})
+            self.assert_(date4)
+            self.assertEqual(date4.date, default_date)
+
+            self.date.write([date1], {
+                    'date': yesterday,
+                    })
+            self.assertEqual(date1.date, yesterday)
+
+            self.date.write([date2], {
+                    'date': today,
+                    })
+            self.assertEqual(date2.date, today)
+
+            self.assertRaises(Exception, self.date.create, {
+                    'date': 'test',
+                    })
+
+            self.assertRaises(Exception, self.date.write, [date1], {
+                    'date': 'test',
+                    })
+
+            self.assertRaises(Exception, self.date.create, {
+                    'date': 1,
+                    })
+
+            self.assertRaises(Exception, self.date.write, [date1], {
+                    'date': 1,
+                    })
+
+            self.assertRaises(Exception, self.date.create, {
+                    'date': datetime.datetime.now(),
+                    })
+
+            self.assertRaises(Exception, self.date.write, [date1], {
+                    'date': datetime.datetime.now(),
+                    })
+
+            self.assertRaises(Exception, self.date.create, {
+                    'date': '2009-13-01',
+                    })
+
+            self.assertRaises(Exception, self.date.write, [date1], {
+                    'date': '2009-02-29',
+                    })
+
+            date5 = self.date.create({
+                    'date': '2009-01-01',
+                    })
+            self.assert_(date5)
+            self.assertEqual(date5.date, datetime.date(2009, 1, 1))
+
+            self.assertRaises(Exception, self.date_required.create, {})
             transaction.cursor.rollback()
 
-            date6_id = self.date_required.create({
-                'date': today,
-                })
-            self.assert_(date6_id)
-
-            date7_id = self.date.create({
-                'date': None,
-                })
-            self.assert_(date7_id)
-
-            date8_id = self.date.create({
-                'date': None,
-                })
-            self.assert_(date8_id)
+            date6 = self.date_required.create({
+                    'date': today,
+                    })
+            self.assert_(date6)
+
+            date7 = self.date.create({
+                    'date': None,
+                    })
+            self.assert_(date7)
+
+            date8 = self.date.create({
+                    'date': None,
+                    })
+            self.assert_(date8)
 
             transaction.cursor.rollback()
 
@@ -1846,270 +1750,258 @@
             yesterday = today - datetime.timedelta(1)
             default_datetime = datetime.datetime(2000, 1, 1, 12, 0, 0)
 
-            datetime1_id = self.datetime.create({
-                'datetime': today,
-                })
-            self.assert_(datetime1_id)
-
-            datetime1 = self.datetime.read(datetime1_id, ['datetime'])
-            self.assert_(datetime1['datetime'] == today)
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '=', today),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '=', tomorrow),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '=', False),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '!=', today),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '!=', tomorrow),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '!=', False),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', [today]),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', [tomorrow]),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', [False]),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', []),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', [today]),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', [tomorrow]),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', [False]),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', []),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<', tomorrow),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<', yesterday),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<', today),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<=', today),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<=', yesterday),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<=', tomorrow),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>', tomorrow),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>', yesterday),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>', today),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>=', tomorrow),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>=', yesterday),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>=', today),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime2_id = self.datetime.create({
-                'datetime': yesterday,
-                })
-            self.assert_(datetime2_id)
-
-            datetime2 = self.datetime.read(datetime2_id, ['datetime'])
-            self.assert_(datetime2['datetime'] == yesterday)
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '=', yesterday),
-                ])
-            self.assert_(datetime_ids == [datetime2_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', [yesterday, today]),
-                ])
-            self.assert_(datetime_ids == [datetime1_id, datetime2_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', [yesterday, today]),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime3_id = self.datetime.create({})
-            self.assert_(datetime3_id)
-
-            datetime3 = self.datetime.read(datetime3_id, ['datetime'])
-            self.assert_(datetime3['datetime'] == None)
-
-            datetime4_id = self.datetime_default.create({})
-            self.assert_(datetime4_id)
-
-            datetime4 = self.datetime_default.read(datetime4_id, ['datetime'])
-            self.assert_(datetime4['datetime'] == default_datetime)
-
-            self.datetime.write(datetime1_id, {
-                'datetime': yesterday,
-                })
-            datetime1 = self.datetime.read(datetime1_id, ['datetime'])
-            self.assert_(datetime1['datetime'] == yesterday)
-
-            self.datetime.write(datetime2_id, {
-                'datetime': today,
-                })
-            datetime2 = self.datetime.read(datetime2_id, ['datetime'])
-            self.assert_(datetime2['datetime'] == today)
-
-            self.failUnlessRaises(Exception, self.datetime.create, {
-                'datetime': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': 'test',
+            datetime1 = self.datetime.create({
+                    'datetime': today,
                     })
-
-            self.failUnlessRaises(Exception, self.datetime.create, {
-                'datetime': 1,
-                })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': 1,
+            self.assert_(datetime1)
+            self.assertEqual(datetime1.datetime, today)
+
+            datetimes = self.datetime.search([
+                    ('datetime', '=', today),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '=', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '=', None),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '!=', today),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '!=', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '!=', None),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', [today]),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', [tomorrow]),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', [None]),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', []),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', [today]),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', [tomorrow]),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', [None]),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', []),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<', yesterday),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<', today),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<=', today),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<=', yesterday),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<=', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>', yesterday),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>', today),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>=', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>=', yesterday),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>=', today),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetime2 = self.datetime.create({
+                    'datetime': yesterday,
                     })
-
-            self.failUnlessRaises(Exception, self.datetime.create, {
-                'datetime': datetime.date.today(),
-                })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': datetime.date.today(),
+            self.assert_(datetime2)
+            self.assertEqual(datetime2.datetime, yesterday)
+
+            datetimes = self.datetime.search([
+                    ('datetime', '=', yesterday),
+                    ])
+            self.assertEqual(datetimes, [datetime2])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', [yesterday, today]),
+                    ])
+            self.assertEqual(datetimes, [datetime1, datetime2])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', [yesterday, today]),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetime3 = self.datetime.create({})
+            self.assert_(datetime3)
+            self.assertEqual(datetime3.datetime, None)
+
+            datetime4 = self.datetime_default.create({})
+            self.assert_(datetime4)
+            self.assertEqual(datetime4.datetime, default_datetime)
+
+            self.datetime.write([datetime1], {
+                    'datetime': yesterday,
                     })
-
-            self.failUnlessRaises(Exception, self.datetime.create, {
-                'datetime': '2009-13-01 12:30:00',
-                })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': '2009-02-29 12:30:00',
+            self.assertEqual(datetime1.datetime, yesterday)
+
+            self.datetime.write([datetime2], {
+                    'datetime': today,
                     })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': '2009-01-01 25:00:00',
+            self.assertEqual(datetime2.datetime, today)
+
+            self.assertRaises(Exception, self.datetime.create, {
+                    'datetime': 'test',
                     })
 
-            datetime5_id = self.datetime.create({
-                'datetime': '2009-01-01 12:00:00',
-                })
-            self.assert_(datetime5_id)
-            datetime5 = self.datetime.read(datetime5_id, ['datetime'])
-            self.assertEqual(datetime5['datetime'],
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': 'test',
+                    })
+
+            self.assertRaises(Exception, self.datetime.create, {
+                    'datetime': 1,
+                    })
+
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': 1,
+                    })
+
+            self.assertRaises(Exception, self.datetime.create, {
+                    'datetime': datetime.date.today(),
+                    })
+
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': datetime.date.today(),
+                    })
+
+            self.assertRaises(Exception, self.datetime.create, {
+                    'datetime': '2009-13-01 12:30:00',
+                    })
+
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': '2009-02-29 12:30:00',
+                    })
+
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': '2009-01-01 25:00:00',
+                    })
+
+            datetime5 = self.datetime.create({
+                    'datetime': '2009-01-01 12:00:00',
+                    })
+            self.assert_(datetime5)
+            self.assertEqual(datetime5.datetime,
                 datetime.datetime(2009, 1, 1, 12, 0, 0))
 
-            self.failUnlessRaises(Exception, self.datetime_required.create, {})
+            self.assertRaises(Exception, self.datetime_required.create, {})
             transaction.cursor.rollback()
 
-            datetime6_id = self.datetime_required.create({
-                'datetime': today,
-                })
-            self.assert_(datetime6_id)
-
-            datetime7_id = self.datetime.create({
-                'datetime': None,
-                })
-            self.assert_(datetime7_id)
-
-            datetime8_id = self.datetime.create({
-                'datetime': None,
-                })
-            self.assert_(datetime8_id)
-
-            datetime9_id = self.datetime.create({
-                'datetime': today.replace(microsecond=1),
-                })
-            self.assert_(datetime9_id)
-            datetime9 = self.datetime.read(datetime9_id, ['datetime'])
-            self.assert_(datetime9['datetime'] == today)
+            datetime6 = self.datetime_required.create({
+                    'datetime': today,
+                    })
+            self.assert_(datetime6)
+
+            datetime7 = self.datetime.create({
+                    'datetime': None,
+                    })
+            self.assert_(datetime7)
+
+            datetime8 = self.datetime.create({
+                    'datetime': None,
+                    })
+            self.assert_(datetime8)
+
+            datetime9 = self.datetime.create({
+                    'datetime': today.replace(microsecond=1),
+                    })
+            self.assert_(datetime9)
+            self.assertEqual(datetime9.datetime, today)
 
             # Test format
             self.assert_(self.datetime_format.create({
                         'datetime': datetime.datetime(2009, 1, 1, 12, 30),
                         }))
-            self.failUnlessRaises(Exception, self.datetime_format.create, {
+            self.assertRaises(Exception, self.datetime_format.create, {
                     'datetime': datetime.datetime(2009, 1, 1, 12, 30, 25),
                     })
 
@@ -2126,251 +2018,239 @@
             night = datetime.time(20, 00)
             default_time = datetime.time(16, 30)
 
-            time1_id = self.time.create({
+            time1 = self.time.create({
                     'time': evening,
                     })
-            self.assert_(time1_id)
-
-            time1 = self.time.read(time1_id, ['time'])
-            self.assertEqual(time1['time'], evening)
-
-            time_ids = self.time.search([
-                ('time', '=', evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '=', night),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '=', False),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '!=', evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '!=', night),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '!=', False),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', 'in', [evening]),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', 'in', [night]),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', 'in', [False]),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', 'in', []),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', 'not in', [evening]),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', 'not in', [night]),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', 'not in', [False]),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', 'not in', []),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '<', night),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '<', pre_evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '<', evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '<=', evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '<=', pre_evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '<=', night),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '>', night),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '>', pre_evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '>', evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '>=', night),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '>=', pre_evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '>=', evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time2_id = self.time.create({
-                'time': pre_evening,
-                })
-            self.assert_(time2_id)
-
-            time2 = self.time.read(time2_id, ['time'])
-            self.assert_(time2['time'] == pre_evening)
-
-            time_ids = self.time.search([
-                ('time', '=', pre_evening),
-                ])
-            self.assert_(time_ids == [time2_id])
-
-            time_ids = self.time.search([
-                ('time', 'in', [pre_evening, evening]),
-                ])
-            self.assert_(time_ids == [time1_id, time2_id])
-
-            time_ids = self.time.search([
-                ('time', 'not in', [pre_evening, evening]),
-                ])
-            self.assert_(time_ids == [])
-
-            time3_id = self.time.create({})
-            self.assert_(time3_id)
-
-            time3 = self.time.read(time3_id, ['time'])
-            self.assert_(time3['time'] == None)
-
-            time4_id = self.time_default.create({})
-            self.assert_(time4_id)
-
-            time4 = self.time_default.read(time4_id, ['time'])
-            self.assert_(time4['time'] == default_time)
-
-            self.time.write(time1_id, {
-                'time': pre_evening,
-                })
-            time1 = self.time.read(time1_id, ['time'])
-            self.assert_(time1['time'] == pre_evening)
-
-            self.time.write(time2_id, {
-                'time': evening,
-                })
-            time2 = self.time.read(time2_id, ['time'])
-            self.assert_(time2['time'] == evening)
-
-            self.failUnlessRaises(Exception, self.time.create, {
+            self.assert_(time1)
+            self.assertEqual(time1.time, evening)
+
+            times = self.time.search([
+                    ('time', '=', evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '=', night),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '=', None),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '!=', evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '!=', night),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '!=', None),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', 'in', [evening]),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', 'in', [night]),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', 'in', [None]),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', 'in', []),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', 'not in', [evening]),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', 'not in', [night]),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', 'not in', [None]),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', 'not in', []),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '<', night),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '<', pre_evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '<', evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '<=', evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '<=', pre_evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '<=', night),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '>', night),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '>', pre_evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '>', evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '>=', night),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '>=', pre_evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '>=', evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            time2 = self.time.create({
+                    'time': pre_evening,
+                    })
+            self.assert_(time2)
+            self.assertEqual(time2.time, pre_evening)
+
+            times = self.time.search([
+                    ('time', '=', pre_evening),
+                    ])
+            self.assertEqual(times, [time2])
+
+            times = self.time.search([
+                    ('time', 'in', [pre_evening, evening]),
+                    ])
+            self.assertEqual(times, [time1, time2])
+
+            times = self.time.search([
+                    ('time', 'not in', [pre_evening, evening]),
+                    ])
+            self.assertEqual(times, [])
+
+            time3 = self.time.create({})
+            self.assert_(time3)
+            self.assertEqual(time3.time, None)
+
+            time4 = self.time_default.create({})
+            self.assert_(time4)
+            self.assertEqual(time4.time, default_time)
+
+            self.time.write([time1], {
+                    'time': pre_evening,
+                    })
+            self.assertEqual(time1.time, pre_evening)
+
+            self.time.write([time2], {
+                    'time': evening,
+                    })
+            self.assertEqual(time2.time, evening)
+
+            self.assertRaises(Exception, self.time.create, {
                     'time': 'test',
                     })
 
-            self.failUnlessRaises(Exception, self.time.write, time1_id,
+            self.assertRaises(Exception, self.time.write, [time1],
                 {
                     'time': 'test',
                     })
 
-            self.failUnlessRaises(Exception, self.time.create, {
+            self.assertRaises(Exception, self.time.create, {
                     'time': 1,
                     })
 
-            self.failUnlessRaises(Exception, self.time.write, time1_id,
+            self.assertRaises(Exception, self.time.write, [time1],
                 {
                     'time': 1,
                     })
 
-            self.failUnlessRaises(Exception, self.time.write, time1_id,
+            self.assertRaises(Exception, self.time.write, [time1],
                 {
                     'time': '25:00:00',
                     })
 
-            time5_id = self.time.create({
-                'time': '12:00:00',
-                })
-            self.assert_(time5_id)
-            time5 = self.time.read(time5_id, ['time'])
-            self.assert_(time5['time'] == datetime.time(12, 0))
-
-            self.failUnlessRaises(Exception, self.time_required.create, {})
+            time5 = self.time.create({
+                    'time': '12:00:00',
+                    })
+            self.assert_(time5)
+            self.assertEqual(time5.time, datetime.time(12, 0))
+
+            self.assertRaises(Exception, self.time_required.create, {})
             transaction.cursor.rollback()
 
-            time6_id = self.time_required.create({
-                'time': evening,
-                })
-            self.assert_(time6_id)
-
-            time7_id = self.time.create({
-                'time': None,
-                })
-            self.assert_(time7_id)
-
-            time8_id = self.time.create({
-                'time': False,
-                })
-            self.assert_(time8_id)
-
-            time9_id = self.time.create({
-                'time': evening.replace(microsecond=1),
-                })
-            self.assert_(time9_id)
-            time9 = self.time.read(time9_id, ['time'])
-            self.assert_(time9['time'] == evening)
+            time6 = self.time_required.create({
+                    'time': evening,
+                    })
+            self.assert_(time6)
+
+            time7 = self.time.create({
+                    'time': None,
+                    })
+            self.assert_(time7)
+
+            time8 = self.time.create({
+                    'time': False,
+                    })
+            self.assert_(time8)
+
+            time9 = self.time.create({
+                    'time': evening.replace(microsecond=1),
+                    })
+            self.assert_(time9)
+            self.assertEqual(time9.time, evening)
 
             # Test format
             self.assert_(self.time_format.create({
                         'time': datetime.time(12, 30),
                         }))
-            self.failUnlessRaises(Exception, self.time_format.create, {
+            self.assertRaises(Exception, self.time_format.create, {
                     'time': datetime.time(12, 30, 25),
                     })
 
@@ -2382,122 +2262,108 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target1_id = self.one2one_target.create({
-                'name': 'target1',
-                })
-            one2one1_id = self.one2one.create({
-                'name': 'origin1',
-                'one2one': target1_id,
-                })
-            self.assert_(one2one1_id)
-
-            one2one1 = self.one2one.read(one2one1_id, ['one2one',
-                'one2one.name'])
-            self.assert_(one2one1['one2one'] == target1_id)
-            self.assert_(one2one1['one2one.name'] == 'target1')
-
-            one2one_ids = self.one2one.search([
-                ('one2one', '=', 'target1'),
-                ])
-            self.assert_(one2one_ids == [one2one1_id])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', '!=', 'target1'),
-                ])
-            self.assert_(one2one_ids == [])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', 'in', [target1_id]),
-                ])
-            self.assert_(one2one_ids == [one2one1_id])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', 'in', [0]),
-                ])
-            self.assert_(one2one_ids == [])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', 'not in', [target1_id]),
-                ])
-            self.assert_(one2one_ids == [])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', 'not in', [0]),
-                ])
-            self.assert_(one2one_ids == [one2one1_id])
-
-            one2one_ids = self.one2one.search([
-                ('one2one.name', '=', 'target1'),
-                ])
-            self.assert_(one2one_ids == [one2one1_id])
-
-            one2one_ids = self.one2one.search([
-                ('one2one.name', '!=', 'target1'),
-                ])
-            self.assert_(one2one_ids == [])
-
-            one2one = self.one2one.browse(one2one1_id)
-            self.assert_(one2one.one2one.name == 'target1')
-
-            one2one2_id = self.one2one.create({
-                'name': 'origin2',
-                })
-            self.assert_(one2one2_id)
-
-            one2one2 = self.one2one.read(one2one2_id, ['one2one'])
-            self.assert_(one2one2['one2one'] == False)
-
-            one2one_ids = self.one2one.search([
-                ('one2one', '=', False),
-                ])
-            self.assert_(one2one_ids == [one2one2_id])
-
-            target2_id = self.one2one_target.create({
-                'name': 'target2',
-                })
-            self.one2one.write(one2one2_id, {
-                'one2one': target2_id,
-                })
-            target2_id = self.one2one_target.search([
-                ('name', '=', 'target2'),
-                ])[0]
-            one2one2 = self.one2one.read(one2one2_id, ['one2one'])
-            self.assert_(one2one2['one2one'] == target2_id)
-
-            self.one2one.write(one2one2_id, {
-                'one2one': False,
-                })
-            one2one2 = self.one2one.read(one2one2_id, ['one2one'])
-            self.assert_(one2one2['one2one'] == False)
-
-            one2one2 = self.one2one.browse(one2one2_id)
-            self.assert_(not one2one2.one2one)
-
-            self.failUnlessRaises(Exception, self.one2one.create, {
-                'name': 'one2one3',
-                'one2one': target1_id,
-                })
+            target1 = self.one2one_target.create({
+                    'name': 'target1',
+                    })
+            one2one1 = self.one2one.create({
+                    'name': 'origin1',
+                    'one2one': target1.id,
+                    })
+            self.assert_(one2one1)
+            self.assertEqual(one2one1.one2one, target1)
+
+            self.assertEqual(self.one2one.read([one2one1.id],
+                    ['one2one.name'])[0]['one2one.name'], 'target1')
+
+            one2ones = self.one2one.search([
+                    ('one2one', '=', 'target1'),
+                    ])
+            self.assertEqual(one2ones, [one2one1])
+
+            one2ones = self.one2one.search([
+                    ('one2one', '!=', 'target1'),
+                    ])
+            self.assertEqual(one2ones, [])
+
+            one2ones = self.one2one.search([
+                    ('one2one', 'in', [target1.id]),
+                    ])
+            self.assertEqual(one2ones, [one2one1])
+
+            one2ones = self.one2one.search([
+                    ('one2one', 'in', [0]),
+                    ])
+            self.assertEqual(one2ones, [])
+
+            one2ones = self.one2one.search([
+                    ('one2one', 'not in', [target1.id]),
+                    ])
+            self.assertEqual(one2ones, [])
+
+            one2ones = self.one2one.search([
+                    ('one2one', 'not in', [0]),
+                    ])
+            self.assertEqual(one2ones, [one2one1])
+
+            one2ones = self.one2one.search([
+                    ('one2one.name', '=', 'target1'),
+                    ])
+            self.assertEqual(one2ones, [one2one1])
+
+            one2ones = self.one2one.search([
+                    ('one2one.name', '!=', 'target1'),
+                    ])
+            self.assertEqual(one2ones, [])
+
+            one2one2 = self.one2one.create({
+                    'name': 'origin2',
+                    })
+            self.assert_(one2one2)
+            self.assertEqual(one2one2.one2one, None)
+
+            one2ones = self.one2one.search([
+                    ('one2one', '=', None),
+                    ])
+            self.assertEqual(one2ones, [one2one2])
+
+            target2 = self.one2one_target.create({
+                    'name': 'target2',
+                    })
+            self.one2one.write([one2one2], {
+                    'one2one': target2.id,
+                    })
+            self.assertEqual(one2one2.one2one, target2)
+
+            self.one2one.write([one2one2], {
+                    'one2one': None,
+                    })
+            self.assertEqual(one2one2.one2one, None)
+
+            self.assertRaises(Exception, self.one2one.create, {
+                    'name': 'one2one3',
+                    'one2one': target1.id,
+                    })
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.one2one.write, one2one2_id, {
-                'one2one': target1_id,
-                })
+            self.assertRaises(Exception, self.one2one.write, [one2one2], {
+                    'one2one': target1.id,
+                    })
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.one2one_required.create, {
-                'name': 'one2one3',
-                })
+            self.assertRaises(Exception, self.one2one_required.create, {
+                    'name': 'one2one3',
+                    })
             transaction.cursor.rollback()
 
-            target3_id = self.one2one_target.create({
-                'name': 'target3_id',
-                })
-
-            one2one3_id = self.one2one_required.create({
-                'name': 'one2one3',
-                'one2one': target3_id,
-                })
-            self.assert_(one2one3_id)
+            target3 = self.one2one_target.create({
+                    'name': 'target3',
+                    })
+
+            one2one3 = self.one2one_required.create({
+                    'name': 'one2one3',
+                    'one2one': target3.id,
+                    })
+            self.assert_(one2one3)
 
             transaction.cursor.rollback()
 
@@ -2511,7 +2377,7 @@
                     (self.one2many, self.one2many_target),
                     (self.one2many_reference, self.one2many_reference_target),
                     ):
-                one2many1_id = one2many.create({
+                one2many1 = one2many.create({
                         'name': 'origin1',
                         'targets': [
                             ('create', {
@@ -2519,149 +2385,140 @@
                                     }),
                             ],
                         })
-                self.assert_(one2many1_id)
-
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(len(one2many1['targets']), 1)
-                target1_id, = one2many1['targets']
+                self.assert_(one2many1)
+
+                self.assertEqual(len(one2many1.targets), 1)
+                target1, = one2many1.targets
 
                 # Try with target1 stored in cache
-                target1 = one2many_target.browse(target1_id)
+                target1 = one2many_target(target1.id)
                 target1.origin
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [target1_id])
-
-                one2many_ids = one2many.search([
+                one2many1 = one2many(one2many1)
+                self.assertEqual(one2many1.targets, (target1,))
+
+                one2manys = one2many.search([
                         ('targets', '=', 'target1'),
                         ])
-                self.assertEqual(one2many_ids, [one2many1_id])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [one2many1])
+
+                one2manys = one2many.search([
                         ('targets', '!=', 'target1'),
                         ])
-                self.assertEqual(one2many_ids, [])
-
-                one2many_ids = one2many.search([
-                        ('targets', 'in', [target1_id]),
+                self.assertEqual(one2manys, [])
+
+                one2manys = one2many.search([
+                        ('targets', 'in', [target1.id]),
                         ])
-                self.assertEqual(one2many_ids, [one2many1_id])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [one2many1])
+
+                one2manys = one2many.search([
                         ('targets', 'in', [0]),
                         ])
-                self.assertEqual(one2many_ids, [])
-
-                one2many_ids = one2many.search([
-                        ('targets', 'not in', [target1_id]),
+                self.assertEqual(one2manys, [])
+
+                one2manys = one2many.search([
+                        ('targets', 'not in', (target1.id,)),
                         ])
-                self.assertEqual(one2many_ids, [])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [])
+
+                one2manys = one2many.search([
                         ('targets', 'not in', [0]),
                         ])
-                self.assertEqual(one2many_ids, [one2many1_id])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [one2many1])
+
+                one2manys = one2many.search([
                         ('targets.name', '=', 'target1'),
                         ])
-                self.assertEqual(one2many_ids, [one2many1_id])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [one2many1])
+
+                one2manys = one2many.search([
                         ('targets.name', '!=', 'target1'),
                         ])
-                self.assertEqual(one2many_ids, [])
-
-                one2many2_id = one2many.create({
+                self.assertEqual(one2manys, [])
+
+                one2many2 = one2many.create({
                         'name': 'origin2',
                         })
-                self.assert_(one2many2_id)
-
-                one2many2 = one2many.read(one2many2_id, ['targets'])
-                self.assertEqual(one2many2['targets'], [])
-
-                one2many_ids = one2many.search([
+                self.assert_(one2many2)
+
+                self.assertEqual(one2many2.targets, ())
+
+                one2manys = one2many.search([
                         ('targets', '=', None),
                         ])
-                self.assertEqual(one2many_ids, [one2many2_id])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(one2manys, [one2many2])
+
+                one2many.write([one2many1], {
                         'targets': [
-                            ('write', [target1_id], {
+                            ('write', [target1.id], {
                                     'name': 'target1bis',
                                     }),
                             ],
                         })
-                target1 = one2many_target.read(target1_id, ['name'])
-                self.assertEqual(target1['name'], 'target1bis')
-
-                target2_id = one2many_target.create({
+                self.assertEqual(target1.name, 'target1bis')
+
+                target2 = one2many_target.create({
                         'name': 'target2',
                         })
-                one2many.write(one2many1_id, {
+                one2many.write([one2many1], {
                         'targets': [
-                            ('add', [target2_id]),
+                            ('add', [target2.id]),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'],
-                    [target1_id, target2_id])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(one2many1.targets,
+                    (target1, target2))
+
+                one2many.write([one2many1], {
                         'targets': [
-                            ('unlink', [target2_id]),
+                            ('unlink', [target2.id]),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [target1_id])
-                target2_id, = one2many_target.search([
-                        ('id', '=', target2_id),
+                self.assertEqual(one2many1.targets, (target1,))
+                target2, = one2many_target.search([
+                        ('id', '=', target2.id),
                         ])
-                self.assert_(target2_id)
-
-                one2many.write(one2many1_id, {
+                self.assert_(target2)
+
+                one2many.write([one2many1], {
                         'targets': [
                             ('unlink_all',),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [])
-                target_ids = one2many_target.search([
-                        ('id', 'in', [target1_id, target2_id]),
+                self.assertEqual(one2many1.targets, ())
+                targets = one2many_target.search([
+                        ('id', 'in', [target1.id, target2.id]),
                         ])
-                self.assertEqual(target_ids, [target1_id, target2_id])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(targets, [target1, target2])
+
+                one2many.write([one2many1], {
                         'targets': [
-                            ('set', [target1_id, target2_id]),
+                            ('set', [target1.id, target2.id]),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'],
-                    [target1_id, target2_id])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(one2many1.targets,
+                    (target1, target2))
+
+                one2many.write([one2many1], {
                         'targets': [
-                            ('delete', [target2_id]),
+                            ('delete', [target2.id]),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [target1_id])
-                target_ids = one2many_target.search([
-                        ('id', '=', target2_id),
+                self.assertEqual(one2many1.targets, (target1,))
+                targets = one2many_target.search([
+                        ('id', '=', target2.id),
                         ])
-                self.assertEqual(target_ids, [])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(targets, [])
+
+                one2many.write([one2many1], {
                         'targets': [
                             ('delete_all',),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [])
-                target_ids = one2many_target.search([
-                        ('id', '=', target1_id),
+                self.assertEqual(one2many1.targets, ())
+                targets = one2many_target.search([
+                        ('id', '=', target1.id),
                         ])
-                self.assertEqual(target_ids, [])
+                self.assertEqual(targets, [])
 
                 transaction.cursor.rollback()
 
@@ -2708,7 +2565,7 @@
                     (self.many2many_reference,
                         self.many2many_reference_target),
                     ):
-                many2many1_id = many2many.create({
+                many2many1 = many2many.create({
                         'name': 'origin1',
                         'targets': [
                             ('create', {
@@ -2716,143 +2573,134 @@
                                     }),
                             ],
                         })
-                self.assert_(many2many1_id)
-
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(len(many2many1['targets']), 1)
-                target1_id, = many2many1['targets']
-
-                many2many_ids = many2many.search([
+                self.assert_(many2many1)
+
+                self.assertEqual(len(many2many1.targets), 1)
+                target1, = many2many1.targets
+
+                many2manys = many2many.search([
                         ('targets', '=', 'target1'),
                         ])
-                self.assertEqual(many2many_ids, [many2many1_id])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [many2many1])
+
+                many2manys = many2many.search([
                         ('targets', '!=', 'target1'),
                         ])
-                self.assertEqual(many2many_ids, [])
-
-                many2many_ids = many2many.search([
-                        ('targets', 'in', [target1_id]),
+                self.assertEqual(many2manys, [])
+
+                many2manys = many2many.search([
+                        ('targets', 'in', [target1.id]),
                         ])
-                self.assertEqual(many2many_ids, [many2many1_id])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [many2many1])
+
+                many2manys = many2many.search([
                         ('targets', 'in', [0]),
                         ])
-                self.assertEqual(many2many_ids, [])
-
-                many2many_ids = many2many.search([
-                        ('targets', 'not in', [target1_id]),
+                self.assertEqual(many2manys, [])
+
+                many2manys = many2many.search([
+                        ('targets', 'not in', [target1.id]),
                         ])
-                self.assertEqual(many2many_ids, [])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [])
+
+                many2manys = many2many.search([
                         ('targets', 'not in', [0]),
                         ])
-                self.assertEqual(many2many_ids, [many2many1_id])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [many2many1])
+
+                many2manys = many2many.search([
                         ('targets.name', '=', 'target1'),
                         ])
-                self.assertEqual(many2many_ids, [many2many1_id])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [many2many1])
+
+                many2manys = many2many.search([
                         ('targets.name', '!=', 'target1'),
                         ])
-                self.assertEqual(many2many_ids, [])
-
-                many2many2_id = many2many.create({
+                self.assertEqual(many2manys, [])
+
+                many2many2 = many2many.create({
                         'name': 'origin2',
                         })
-                self.assert_(many2many2_id)
-
-                many2many2 = many2many.read(many2many2_id, ['targets'])
-                self.assertEqual(many2many2['targets'], [])
-
-                many2many_ids = many2many.search([
+                self.assert_(many2many2)
+
+                self.assertEqual(many2many2.targets, ())
+
+                many2manys = many2many.search([
                         ('targets', '=', None),
                         ])
-                self.assertEqual(many2many_ids, [many2many2_id])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(many2manys, [many2many2])
+
+                many2many.write([many2many1], {
                         'targets': [
-                            ('write', [target1_id], {
+                            ('write', [target1.id], {
                                     'name': 'target1bis',
                                     }),
                             ],
                         })
-                target1 = many2many_target.read(target1_id, ['name'])
-                self.assertEqual(target1['name'], 'target1bis')
-
-                target2_id = many2many_target.create({
+                self.assertEqual(target1.name, 'target1bis')
+
+                target2 = many2many_target.create({
                         'name': 'target2',
                         })
-                many2many.write(many2many1_id, {
+                many2many.write([many2many1], {
                         'targets': [
-                            ('add', [target2_id]),
+                            ('add', [target2.id]),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'],
-                    [target1_id, target2_id])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(many2many1.targets,
+                    (target1, target2))
+
+                many2many.write([many2many1], {
                         'targets': [
-                            ('unlink', [target2_id]),
+                            ('unlink', [target2.id]),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'], [target1_id])
-                target2_id, = many2many_target.search([
-                        ('id', '=', target2_id),
+                self.assertEqual(many2many1.targets, (target1,))
+                target2, = many2many_target.search([
+                        ('id', '=', target2.id),
                         ])
-                self.assert_(target2_id)
-
-                many2many.write(many2many1_id, {
+                self.assert_(target2)
+
+                many2many.write([many2many1], {
                         'targets': [
                             ('unlink_all',),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'], [])
-                target_ids = many2many_target.search([
-                        ('id', 'in', [target1_id, target2_id]),
+                self.assertEqual(many2many1.targets, ())
+                targets = many2many_target.search([
+                        ('id', 'in', [target1.id, target2.id]),
                         ])
-                self.assertEqual(target_ids, [target1_id, target2_id])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(targets, [target1, target2])
+
+                many2many.write([many2many1], {
                         'targets': [
-                            ('set', [target1_id, target2_id]),
+                            ('set', [target1.id, target2.id]),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'],
-                    [target1_id, target2_id])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(many2many1.targets,
+                    (target1, target2))
+
+                many2many.write([many2many1], {
                         'targets': [
-                            ('delete', [target2_id]),
+                            ('delete', [target2.id]),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'], [target1_id])
-                target_ids = many2many_target.search([
-                        ('id', '=', target2_id),
+                self.assertEqual(many2many1.targets, (target1,))
+                targets = many2many_target.search([
+                        ('id', '=', target2.id),
                         ])
-                self.assertEqual(target_ids, [])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(targets, [])
+
+                many2many.write([many2many1], {
                         'targets': [
                             ('delete_all',),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'], [])
-                target_ids = many2many_target.search([
-                        ('id', '=', target1_id),
+                self.assertEqual(many2many1.targets, ())
+                targets = many2many_target.search([
+                        ('id', '=', target1.id),
                         ])
-                self.assertEqual(target_ids, [])
+                self.assertEqual(targets, [])
 
                 transaction.cursor.rollback()
 
@@ -2891,140 +2739,125 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target1_id = self.reference_target.create({
+            target1 = self.reference_target.create({
                     'name': 'target1',
                     })
-            reference1_id = self.reference.create({
+            reference1 = self.reference.create({
                     'name': 'reference1',
-                    'reference': 'test.reference.target,%s' % target1_id,
+                    'reference': str(target1),
                     })
-            self.assert_(reference1_id)
-
-            reference1 = self.reference.read(reference1_id, ['reference'])
-            self.assertEqual(reference1['reference'],
-                'test.reference.target,%s' % target1_id)
-
-            reference_ids = self.reference.search([
-                    ('reference', '=',
-                        'test.reference.target,%s' % target1_id),
+            self.assert_(reference1)
+
+            self.assertEqual(reference1.reference, target1)
+
+            references = self.reference.search([
+                    ('reference', '=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
-                    ('reference', '=',
-                        ('test.reference.target', target1_id)),
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
+                    ('reference', '=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
-                    ('reference', '=',
-                        ['test.reference.target', target1_id]),
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
+                    ('reference', '=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
-                    ('reference', '!=',
-                        'test.reference.target,%s' % target1_id),
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
+                    ('reference', '!=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
-                    ('reference', '!=',
-                        ('test.reference.target', target1_id)),
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
+                    ('reference', '!=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
+                    ('reference', 'in', [str(target1)]),
+                    ])
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
                     ('reference', 'in',
-                        ['test.reference.target,%s' % target1_id]),
+                        [('test.reference.target', target1.id)]),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
-                    ('reference', 'in',
-                        [('test.reference.target', target1_id)]),
-                    ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
                     ('reference', 'in', [None]),
                     ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
+                    ('reference', 'not in', [str(target1)]),
+                    ])
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
                     ('reference', 'not in',
-                        ['test.reference.target,%s' % target1_id]),
+                        [('test.reference.target', target1.id)]),
                     ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
-                    ('reference', 'not in',
-                        [('test.reference.target', target1_id)]),
-                    ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
                     ('reference', 'not in', [None]),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference2_id = self.reference.create({
+            self.assertEqual(references, [reference1])
+
+            reference2 = self.reference.create({
                     'name': 'reference2',
                     })
-            self.assert_(reference2_id)
-
-            reference2 = self.reference.read(reference2_id, ['reference'])
-            self.assertEqual(reference2['reference'], None)
-
-            reference_ids = self.reference.search([
+            self.assert_(reference2)
+
+            self.assertEqual(reference2.reference, None)
+
+            references = self.reference.search([
                     ('reference', '=', None),
                     ])
-            self.assertEqual(reference_ids, [reference2_id])
-
-            target2_id = self.reference_target.create({
+            self.assertEqual(references, [reference2])
+
+            target2 = self.reference_target.create({
                     'name': 'target2',
                     })
 
-            self.reference.write(reference2_id, {
-                    'reference': 'test.reference.target,%s' % target2_id,
+            self.reference.write([reference2], {
+                    'reference': str(target2),
                     })
-            reference2 = self.reference.read(reference2_id, ['reference'])
-            self.assertEqual(reference2['reference'],
-                'test.reference.target,%s' % target2_id)
-
-            self.reference.write(reference2_id, {
+            self.assertEqual(reference2.reference, target2)
+
+            self.reference.write([reference2], {
                     'reference': None,
                     })
-            reference2 = self.reference.read(reference2_id, ['reference'])
-            self.assertEqual(reference2['reference'], None)
-
-            self.reference.write(reference2_id, {
-                    'reference': ('test.reference.target', target2_id),
+            self.assertEqual(reference2.reference, None)
+
+            self.reference.write([reference2], {
+                    'reference': ('test.reference.target', target2.id),
                     })
-            reference2 = self.reference.read(reference2_id, ['reference'])
-            self.assertEqual(reference2['reference'],
-                'test.reference.target,%s' % target2_id)
-
-            reference3_id = self.reference.create({
+            self.assertEqual(reference2.reference, target2)
+
+            reference3 = self.reference.create({
                     'name': 'reference3',
-                    'reference': ('test.reference.target', target1_id),
+                    'reference': ('test.reference.target', target1.id),
                     })
-            self.assert_(reference3_id)
+            self.assert_(reference3)
 
             self.assertRaises(Exception, self.reference_required.create, {
                     'name': 'reference4',
                     })
             transaction.cursor.rollback()
 
-            target4_id = self.reference_target.create({
+            target4 = self.reference_target.create({
                     'name': 'target4_id',
                     })
 
-            reference4_id = self.reference_required.create({
+            reference4 = self.reference_required.create({
                     'name': 'reference4',
-                    'reference': 'test.reference.target,%s' % target4_id,
+                    'reference': str(target4),
                     })
-            self.assert_(reference4_id)
+            self.assert_(reference4)
 
             transaction.cursor.rollback()
 
@@ -3036,272 +2869,264 @@
                 context=CONTEXT) as transaction:
 
             # Test Char
-            prop_id_a = self.property_.create({'char': 'Test'})
-            self.assert_(prop_id_a)
-
-            prop_id_b = self.property_.create({})
-            self.assert_(prop_id_b)
-
-            prop_id_c = self.property_.create({'char': 'FooBar'})
-            self.assert_(prop_id_c)
-
-            prop_a = self.property_.read(prop_id_a, ['char'])
-            self.assert_(prop_a['char'] == 'Test')
-
-            prop_ids = self.property_.search([('char', '=', 'Test')])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('char', '=', False)])
-            self.assert_(prop_ids == [prop_id_b])
-
-            prop_ids = self.property_.search([('char', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'like', 'Tes%')])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('char', 'like', '%Bar')])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'not like', 'Tes%')])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'ilike', 'tes%')])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('char', 'ilike', '%bar')])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'not ilike', 'tes%')])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'in', ['Test'])])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            prop_a = self.property_.create({'char': 'Test'})
+            self.assert_(prop_a)
+            self.assertEqual(prop_a.char, 'Test')
+
+            prop_b = self.property_.create({})
+            self.assert_(prop_b)
+            self.assertEqual(prop_b.char, None)
+
+            prop_c = self.property_.create({'char': 'FooBar'})
+            self.assert_(prop_c)
+            self.assertEqual(prop_c.char, 'FooBar')
+
+            props = self.property_.search([('char', '=', 'Test')])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([('char', '=', None)])
+            self.assertEqual(props, [prop_b])
+
+            props = self.property_.search([('char', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_c])
+
+            props = self.property_.search([('char', 'like', 'Tes%')])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([('char', 'like', '%Bar')])
+            self.assertEqual(props, [prop_c])
+
+            props = self.property_.search([('char', 'not like', 'Tes%')])
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([('char', 'ilike', 'tes%')])
+            self.assert_(props, [prop_a])
+
+            props = self.property_.search([('char', 'ilike', '%bar')])
+            self.assertEqual(props, [prop_c])
+
+            props = self.property_.search([('char', 'not ilike', 'tes%')])
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([('char', 'in', ['Test'])])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('char', 'in', ['Test', 'FooBar'])])
-            self.assert_(prop_ids == [prop_id_a, prop_id_c])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_c])
+
+            props = self.property_.search([
                     ('char', 'not in', ['Test', 'FooBar'])])
-            self.assert_(prop_ids == [prop_id_b])
-
-            model_field_obj = POOL.get('ir.model.field')
-            property_obj = POOL.get('ir.property')
+            self.assertEqual(props, [prop_b])
 
             # Test default value
-            property_field_id, = model_field_obj.search([
-                        ('model.model', '=', 'test.property'),
-                        ('name', '=', 'char'),
+            property_field, = self.model_field.search([
+                    ('model.model', '=', 'test.property'),
+                    ('name', '=', 'char'),
                     ], limit=1)
-            property_obj.create({
-                        'field': property_field_id,
-                        'value': ',DEFAULT_VALUE',
+            self.ir_property.create({
+                    'field': property_field.id,
+                    'value': ',DEFAULT_VALUE',
                     })
 
-            prop_id_d = self.property_.create({})
-            self.assert_(prop_id_d)
-
-            prop = self.property_.read(prop_id_d, ['char'])
-            self.assert_(prop['char'] == 'DEFAULT_VALUE')
-
-            prop_ids = self.property_.search([('char', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_c, prop_id_d])
-
-            self.property_.write(prop_id_a, {'char': None})
-            prop_a = self.property_.read(prop_id_a, ['char'])
-            self.assert_(prop_a['char'] == None)
-
-            self.property_.write(prop_id_b, {'char': 'Test'})
-            prop_b = self.property_.read(prop_id_b, ['char'])
-            self.assert_(prop_b['char'] == 'Test')
+            prop_d = self.property_.create({})
+            self.assert_(prop_d)
+            self.assertEqual(prop_d.char, 'DEFAULT_VALUE')
+
+            props = self.property_.search([('char', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_c, prop_d])
+
+            self.property_.write([prop_a], {'char': None})
+            self.assertEqual(prop_a.char, None)
+
+            self.property_.write([prop_b], {'char': 'Test'})
+            self.assertEqual(prop_b.char, 'Test')
 
             transaction.cursor.rollback()
 
             # Test Many2One
-            char_id_a = self.char.create({'char': 'Test'})
-            self.assert_(char_id_a)
-
-            char_id_b = self.char.create({'char': 'FooBar'})
-            self.assert_(char_id_b)
-
-            prop_id_a = self.property_.create({'many2one': char_id_a})
-            self.assert_(prop_id_a)
-
-            prop_id_b = self.property_.create({'many2one': char_id_b})
-            self.assert_(prop_id_b)
-
-            prop_id_c = self.property_.create({})
-            self.assert_(prop_id_c)
-
-            prop_ids = self.property_.search([('many2one', '=', char_id_a)])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('many2one', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([('many2one', '=', False)])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_a = self.property_.read(prop_id_a, ['many2one'])
-            self.assert_(prop_a['many2one'] == char_id_a)
-
-            prop_ids = self.property_.search([
-                    ('many2one', 'in', [char_id_a, char_id_b])])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
-                    ('many2one', 'not in', [char_id_a, char_id_b])])
-            self.assert_(prop_ids == [prop_id_c])
-
-            self.property_.write(prop_id_b, {'many2one': char_id_a})
-            prop_b = self.property_.read(prop_id_b, ['many2one'])
-            self.assert_(prop_b['many2one'] == char_id_a)
+            char_a = self.char.create({'char': 'Test'})
+            self.assert_(char_a)
+
+            char_b = self.char.create({'char': 'FooBar'})
+            self.assert_(char_b)
+
+            prop_a = self.property_.create({'many2one': char_a.id})
+            self.assert_(prop_a)
+            self.assertEqual(prop_a.many2one, char_a)
+
+            prop_b = self.property_.create({'many2one': char_b.id})
+            self.assert_(prop_b)
+            self.assertEqual(prop_b.many2one, char_b)
+
+            prop_c = self.property_.create({})
+            self.assert_(prop_c)
+            self.assertEqual(prop_c.many2one, None)
+
+            props = self.property_.search([('many2one', '=', char_a.id)])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([('many2one', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([('many2one', '=', None)])
+            self.assertEqual(props, [prop_c])
+
+            self.assertEqual(prop_a.many2one, char_a)
+
+            props = self.property_.search([
+                    ('many2one', 'in', [char_a.id, char_b.id])])
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
+                    ('many2one', 'not in', [char_a.id, char_b.id])])
+            self.assertEqual(props, [prop_c])
+
+            self.property_.write([prop_b], {'many2one': char_a.id})
+            self.assertEqual(prop_b.many2one, char_a)
 
             transaction.cursor.rollback()
 
             # Test Numeric
-            prop_id_a = self.property_.create({'numeric': Decimal('1.1')})
-            self.assert_(prop_id_a)
-
-            prop_id_b = self.property_.create({'numeric': Decimal('2.6')})
-            self.assert_(prop_id_b)
-
-            prop_id_c = self.property_.create({})
-            self.assert_(prop_id_c)
-
-            prop_ids = self.property_.search([('numeric', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([('numeric', '=', False)])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_ids = self.property_.search([
+            prop_a = self.property_.create({'numeric': Decimal('1.1')})
+            self.assert_(prop_a)
+            self.assertEqual(prop_a.numeric, Decimal('1.1'))
+
+            prop_b = self.property_.create({'numeric': Decimal('2.6')})
+            self.assert_(prop_b)
+            self.assertEqual(prop_b.numeric, Decimal('2.6'))
+
+            prop_c = self.property_.create({})
+            self.assert_(prop_c)
+            self.assertEqual(prop_c.numeric, None)
+
+            props = self.property_.search([('numeric', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([('numeric', '=', None)])
+            self.assertEqual(props, [prop_c])
+
+            props = self.property_.search([
                     ('numeric', '=', Decimal('1.1')),
                     ])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('numeric', '!=', Decimal('1.1'))])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([
                     ('numeric', '<', Decimal('2.6')),
                     ])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('numeric', '<=', Decimal('2.6'))])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
                     ('numeric', '>', Decimal('1.1')),
                     ])
-            self.assert_(prop_ids == [prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_b])
+
+            props = self.property_.search([
                     ('numeric', '>=', Decimal('1.1'))])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
                     ('numeric', 'in', [Decimal('1.1')])])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('numeric', 'in', [Decimal('1.1'), Decimal('2.6')])])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
                     ('numeric', 'not in', [Decimal('1.1')])])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([
                     ('numeric', 'not in', [Decimal('1.1'), Decimal('2.6')])])
-            self.assert_(prop_ids == [prop_id_c])
+            self.assertEqual(props, [prop_c])
 
             # Test default value
-            property_field_id, = model_field_obj.search([
-                        ('model.model', '=', 'test.property'),
-                        ('name', '=', 'numeric'),
+            property_field, = self.model_field.search([
+                    ('model.model', '=', 'test.property'),
+                    ('name', '=', 'numeric'),
                     ], limit=1)
-            property_obj.create({
-                        'field': property_field_id,
-                        'value': ',3.7',
+            self.ir_property.create({
+                    'field': property_field.id,
+                    'value': ',3.7',
                     })
 
-            prop_id_d = self.property_.create({})
-            self.assert_(prop_id_d)
-
-            prop_d = self.property_.read(prop_id_d, ['numeric'])
-            self.assert_(prop_d['numeric'] == Decimal('3.7'))
-
-            self.property_.write(prop_id_a, {'numeric': None})
-            prop_a = self.property_.read(prop_id_a, ['numeric'])
-            self.assert_(prop_a['numeric'] == None)
-
-            self.property_.write(prop_id_b, {'numeric': Decimal('3.11')})
-            prop_b = self.property_.read(prop_id_b, ['numeric'])
-            self.assert_(prop_b['numeric'] == Decimal('3.11'))
+            prop_d = self.property_.create({})
+            self.assert_(prop_d)
+            self.assertEqual(prop_d.numeric, Decimal('3.7'))
+
+            self.property_.write([prop_a], {'numeric': None})
+            self.assertEqual(prop_a.numeric, None)
+
+            self.property_.write([prop_b], {'numeric': Decimal('3.11')})
+            self.assertEqual(prop_b.numeric, Decimal('3.11'))
 
             transaction.cursor.rollback()
 
             # Test Selection
-            prop_id_a = self.property_.create({'selection': 'option_a'})
-            self.assert_(prop_id_a)
-
-            prop_id_b = self.property_.create({'selection': 'option_b'})
-            self.assert_(prop_id_b)
-
-            prop_id_c = self.property_.create({})
-            self.assert_(prop_id_c)
-
-            prop_ids = self.property_.search([('selection', '=', 'option_a')])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('selection', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([('selection', '=', False)])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_ids = self.property_.search([('selection', '!=', 'option_a')])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([
+            prop_a = self.property_.create({'selection': 'option_a'})
+            self.assert_(prop_a)
+            self.assertEqual(prop_a.selection, 'option_a')
+
+            prop_b = self.property_.create({'selection': 'option_b'})
+            self.assert_(prop_b)
+            self.assertEqual(prop_b.selection, 'option_b')
+
+            prop_c = self.property_.create({})
+            self.assert_(prop_c)
+            self.assertEqual(prop_c.selection, None)
+
+            props = self.property_.search([('selection', '=', 'option_a')])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([('selection', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([('selection', '=', None)])
+            self.assertEqual(props, [prop_c])
+
+            props = self.property_.search([('selection', '!=', 'option_a')])
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([
                     ('selection', 'in', ['option_a'])])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('selection', 'in', ['option_a', 'option_b'])])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
                     ('selection', 'not in', ['option_a'])])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
+            self.assertEqual(props, [prop_b, prop_c])
 
             # Test default value
-            property_field_id, = model_field_obj.search([
-                        ('model.model', '=', 'test.property'),
-                        ('name', '=', 'selection'),
+            property_field, = self.model_field.search([
+                    ('model.model', '=', 'test.property'),
+                    ('name', '=', 'selection'),
                     ], limit=1)
-            property_obj.create({
-                        'field': property_field_id,
-                        'value': ',option_a',
+            self.ir_property.create({
+                    'field': property_field.id,
+                    'value': ',option_a',
                     })
 
-            prop_id_d = self.property_.create({})
-            self.assert_(prop_id_d)
-
-            prop_d = self.property_.read(prop_id_d, ['selection'])
-            self.assert_(prop_d['selection'] == 'option_a')
-
-            self.property_.write(prop_id_a, {'selection': None})
-            prop_a = self.property_.read(prop_id_a, ['selection'])
-            self.assert_(prop_a['selection'] == None)
-
-            self.property_.write(prop_id_c, {'selection': 'option_b'})
-            prop_c = self.property_.read(prop_id_c, ['selection'])
-            self.assert_(prop_c['selection'] == 'option_b')
+            prop_d = self.property_.create({})
+            self.assert_(prop_d)
+            self.assertEqual(prop_d.selection, 'option_a')
+
+            self.property_.write([prop_a], {'selection': None})
+            self.assertEqual(prop_a.selection, None)
+
+            self.property_.write([prop_c], {'selection': 'option_b'})
+            self.assertEqual(prop_c.selection, 'option_b')
 
             transaction.cursor.rollback()
 
diff -r fb51f5b4aa4c trytond/tests/test_mixins.py
--- a/trytond/tests/test_mixins.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_mixins.py	Mon Sep 24 20:36:36 2012 +0200
@@ -25,27 +25,31 @@
     def testModelURL(self):
         "Test model URLs"
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            self.assertEqual(self.urlmodel.get_url(),
+            self.assertEqual(self.urlmodel.__url__,
                 'tryton://%s/%s/model/test.urlobject' % (self.hostname,
                     urllib.quote(DB_NAME)))
 
             server_name = 'michaelscott.paper.test'
             CONFIG['hostname_jsonrpc'] = server_name
-            self.assertEqual(self.urlmodel.get_url(),
+            self.assertEqual(self.urlmodel.__url__,
                 'tryton://%s/%s/model/test.urlobject' % (server_name,
                     urllib.quote(DB_NAME)))
 
+            self.assertEqual(self.urlmodel(1).__url__,
+                'tryton://%s/%s/model/test.urlobject/1' % (server_name,
+                    urllib.quote(DB_NAME)))
+
     def testWizardURL(self):
         "Test wizard URLs"
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
             CONFIG['hostname_jsonrpc'] = None
-            self.assertEqual(self.urlwizard.get_url(),
+            self.assertEqual(self.urlwizard.__url__,
                 'tryton://%s/%s/wizard/test.test_wizard' % (self.hostname,
                     urllib.quote(DB_NAME)))
 
             server_name = 'michaelscott.paper.test'
             CONFIG['hostname_jsonrpc'] = server_name
-            self.assertEqual(self.urlwizard.get_url(),
+            self.assertEqual(self.urlwizard.__url__,
                 'tryton://%s/%s/wizard/test.test_wizard' % (server_name,
                     urllib.quote(DB_NAME)))
 
diff -r fb51f5b4aa4c trytond/tests/test_modelsingleton.py
--- a/trytond/tests/test_modelsingleton.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_modelsingleton.py	Mon Sep 24 20:36:36 2012 +0200
@@ -24,7 +24,7 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            singleton = self.singleton.read(1, ['name'])
+            singleton, = self.singleton.read([1], ['name'])
             self.assert_(singleton['name'] == 'test')
             self.assert_(singleton['id'] == 1)
 
@@ -32,7 +32,7 @@
             self.assert_(singleton['name'] == 'test')
             self.assert_(singleton['id'] == 1)
 
-            singleton = self.singleton.read(1, [
+            singleton, = self.singleton.read([1], [
                 'create_uid',
                 'create_date',
                 'write_uid',
@@ -51,20 +51,17 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            singleton_id = self.singleton.create({'name': 'bar'})
-            self.assert_(singleton_id)
+            singleton = self.singleton.create({'name': 'bar'})
+            self.assert_(singleton)
+            self.assertEqual(singleton.name, 'bar')
 
-            singleton = self.singleton.read(singleton_id, ['name'])
-            self.assert_(singleton['name'] == 'bar')
+            singleton2 = self.singleton.create({'name': 'foo'})
+            self.assertEqual(singleton2, singleton)
 
-            singleton2_id = self.singleton.create({'name': 'foo'})
-            self.assert_(singleton2_id == singleton_id)
+            self.assertEqual(singleton.name, 'foo')
 
-            singleton = self.singleton.read(singleton_id, ['name'])
-            self.assert_(singleton['name'] == 'foo')
-
-            singleton_ids = self.singleton.search([])
-            self.assert_(len(singleton_ids) == 1)
+            singletons = self.singleton.search([])
+            self.assertEqual(singletons, [singleton])
 
             transaction.cursor.rollback()
 
@@ -74,19 +71,19 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            singleton_id = self.singleton.search([])[0]
+            singleton, = self.singleton.search([])
 
-            singleton2_id = self.singleton.copy(singleton_id)
-            self.assert_(singleton2_id == singleton_id)
+            singleton2, = self.singleton.copy([singleton])
+            self.assertEqual(singleton2, singleton)
 
-            singleton_ids = self.singleton.search([])
-            self.assert_(len(singleton_ids) == 1)
+            singletons = self.singleton.search([])
+            self.assertEqual(singletons, [singleton])
 
-            singleton3_id = self.singleton.copy(singleton_id, {'name': 'bar'})
-            self.assert_(singleton3_id == singleton_id)
+            singleton3, = self.singleton.copy([singleton], {'name': 'bar'})
+            self.assertEqual(singleton3, singleton)
 
-            singleton_ids = self.singleton.search([])
-            self.assert_(len(singleton_ids) == 1)
+            singletons = self.singleton.search([])
+            self.assertEqual(singletons, [singleton])
 
             transaction.cursor.rollback()
 
@@ -97,26 +94,26 @@
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             default = self.singleton.default_get(['name'])
-            self.assert_(default == {'name': 'test'})
+            self.assertEqual(default, {'name': 'test'})
 
             default = self.singleton.default_get(['create_uid'])
-            self.assert_(len(default) == 2)
+            self.assertEqual(len(default), 2)
 
             default = self.singleton.default_get(['create_uid'],
                     with_rec_name=False)
-            self.assert_(len(default) == 1)
+            self.assertEqual(len(default), 1)
 
             self.singleton.create({'name': 'bar'})
 
             default = self.singleton.default_get(['name'])
-            self.assert_(default == {'name': 'bar'})
+            self.assertEqual(default, {'name': 'bar'})
 
             default = self.singleton.default_get(['create_uid'])
-            self.assert_(len(default) == 2)
+            self.assertEqual(len(default), 2)
 
             default = self.singleton.default_get(['create_uid'],
                     with_rec_name=False)
-            self.assert_(len(default) == 1)
+            self.assertEqual(len(default), 1)
 
             transaction.cursor.rollback()
 
@@ -126,14 +123,14 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            singleton_ids = self.singleton.search([])
-            self.assert_(singleton_ids == [1])
+            singletons = self.singleton.search([])
+            self.assertEqual(map(int, singletons), [1])
 
-            singleton_ids = self.singleton.search([])
-            self.assert_(singleton_ids == [1])
+            singletons = self.singleton.search([])
+            self.assertEqual(map(int, singletons), [1])
 
-            singleton_ids = self.singleton.search([], count=True)
-            self.assert_(singleton_ids == 1)
+            count = self.singleton.search([], count=True)
+            self.assertEqual(count, 1)
 
             transaction.cursor.rollback()
 
diff -r fb51f5b4aa4c trytond/tests/test_mptt.py
--- a/trytond/tests/test_mptt.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_mptt.py	Mon Sep 24 20:36:36 2012 +0200
@@ -18,80 +18,76 @@
         self.mptt = POOL.get('test.mptt')
 
     def CheckTree(self, parent_id=False, left=0, right=0):
-        child_ids = self.mptt.search([
-            ('parent', '=', parent_id),
-            ])
-        childs = self.mptt.read(child_ids, ['left', 'right'])
-        childs.sort(key=lambda x: child_ids.index(x['id']))
+        childs = self.mptt.search([
+                ('parent', '=', parent_id),
+                ])
+        child_index = dict((x.id, i) for i, x in enumerate(childs))
+        childs.sort(key=lambda x: child_index[x.id])
         for child in childs:
-            assert child['left'] > left, \
-                    'Record (%d): left %d <= parent left %d' % \
-                    (child['id'], child['left'], left)
-            assert child['left'] < child['right'], \
-                    'Record (%d): left %d >= right %d' % \
-                    (child['id'], child['left'], child['right'])
-            assert right == 0 or child['right'] < right, \
-                    'Record (%d): right %d >= parent right %d' % \
-                    (child['id'], child['right'], right)
-            self.CheckTree(child['id'], left=child['left'],
-                    right=child['right'])
+            assert child.left > left, \
+                '%s: left %d <= parent left %d' % \
+                (child, child.left, left)
+            assert child.left < child.right, \
+                '%s: left %d >= right %d' % \
+                (child, child.left, child.right)
+            assert right == 0 or child.right < right, \
+                '%s: right %d >= parent right %d' % \
+                (child, child.right, right)
+            self.CheckTree(child.id, left=child.left,
+                right=child.right)
         next_left = 0
         for child in childs:
-            assert child['left'] > next_left, \
-                    'Record (%d): left %d <= next left %d' % \
-                    (child['id'], child['left'], next_left)
-            next_left = child['right']
+            assert child.left > next_left, \
+                '%s: left %d <= next left %d' % \
+                (child, child.left, next_left)
+            next_left = child.right
         childs.reverse()
         previous_right = 0
         for child in childs:
-            assert previous_right == 0 or child['right'] < previous_right, \
-                'Record (%d): right %d >= previous right %d' \
-                % (child['id'], child['right'], previous_right)
-            previous_right = child['left']
+            assert previous_right == 0 or child.right < previous_right, \
+                '%s: right %d >= previous right %d' \
+                % (child, child.right, previous_right)
+            previous_right = child.left
 
-    def ReParent(self, parent_id=False):
-        record_ids = self.mptt.search([
-            ('parent', '=', parent_id),
-            ])
-        if not record_ids:
+    def ReParent(self, parent=None):
+        records = self.mptt.search([
+                ('parent', '=', parent),
+                ])
+        if not records:
             return
-        for record_id in record_ids:
-            for record2_id in record_ids:
-                if record_id != record2_id:
-                    self.mptt.write(record_id, {
-                        'parent': record2_id,
-                        })
-                    self.mptt.write(record_id, {
-                        'parent': parent_id,
-                        })
-        for record_id in record_ids:
-            self.ReParent(record_id)
+        for record in records:
+            for record2 in records:
+                if record != record2:
+                    record.parent = record2
+                    record.save()
+                    record.parent = parent
+                    record.save()
+        for record in records:
+            self.ReParent(record)
 
-    def ReOrder(self, parent_id=False):
-        record_ids = self.mptt.search([
-            ('parent', '=', parent_id),
-            ])
-        if not record_ids:
+    def ReOrder(self, parent=None):
+        records = self.mptt.search([
+                ('parent', '=', parent),
+                ])
+        if not records:
             return
-        i = len(record_ids)
-        for record_id in record_ids:
-            self.mptt.write(record_id, {
-                'sequence': i,
-                })
+        i = len(records)
+        for record in records:
+            record.sequence = i
+            record.save()
             i -= 1
         i = 0
-        for record_id in record_ids:
-            self.mptt.write(record_id, {
-                'sequence': i,
+        for record in records:
+            record.sequence = i
+            record.save()
+            i += 1
+        for record in records:
+            self.ReOrder(record)
+
+        records = self.mptt.search([])
+        self.mptt.write(records, {
+                'sequence': 0,
                 })
-            i += 1
-        for record_id in record_ids:
-            self.ReOrder(record_id)
-
-        record_ids = self.mptt.search([])
-        self.mptt.write(record_ids, {
-            'sequence': 0,
-            })
 
     def test0010create(self):
         '''
@@ -106,11 +102,12 @@
                 k = 0
                 for parent_record in parent_records:
                     for i in range(3):
-                        record_id = self.mptt.create({
+                        record = self.mptt.create({
                             'name': 'Test %d %d %d' % (j, k, i),
-                            'parent': parent_record,
+                            'parent': (parent_record.id
+                                    if parent_record else None),
                             })
-                        new_records.append(record_id)
+                        new_records.append(record)
                     k += 1
             self.CheckTree()
 
@@ -137,12 +134,11 @@
     def test0040active(self):
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            for record_id in record_ids:
-                if record_id % 2:
-                    self.mptt.write(record_id, {
-                            'active': False
-                            })
+            records = self.mptt.search([])
+            for record in records:
+                if record.id % 2:
+                    record.active = False
+                    record.save()
             self.CheckTree()
             self.ReParent()
             self.CheckTree()
@@ -153,8 +149,8 @@
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            self.mptt.write(record_ids[:len(record_ids) // 2], {
+            records = self.mptt.search([])
+            self.mptt.write(records[:len(records) // 2], {
                     'active': False
                     })
             self.CheckTree()
@@ -163,8 +159,8 @@
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            self.mptt.write(record_ids, {
+            records = self.mptt.search([])
+            self.mptt.write(records, {
                     'active': False
                     })
             self.ReParent()
@@ -180,26 +176,26 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            for record_id in record_ids:
-                if record_id % 2:
-                    self.mptt.delete(record_id)
+            records = self.mptt.search([])
+            for record in records:
+                if record.id % 2:
+                    self.mptt.delete([record])
             self.CheckTree()
 
             transaction.cursor.rollback()
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            self.mptt.delete(record_ids[:len(record_ids) // 2])
+            records = self.mptt.search([])
+            self.mptt.delete(records[:len(records) // 2])
             self.CheckTree()
 
             transaction.cursor.rollback()
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            self.mptt.delete(record_ids)
+            records = self.mptt.search([])
+            self.mptt.delete(records)
             self.CheckTree()
 
             transaction.cursor.rollback()
diff -r fb51f5b4aa4c trytond/tests/test_sequence.py
--- a/trytond/tests/test_sequence.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_sequence.py	Mon Sep 24 20:36:36 2012 +0200
@@ -24,25 +24,25 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            sequence_id = self.sequence.create({
-                'name': 'Test incremental',
-                'code': 'test',
-                'prefix': '',
-                'suffix': '',
-                'type': 'incremental',
-                })
-            self.assertEqual(self.sequence.get_id(sequence_id), '1')
+            sequence = self.sequence.create({
+                    'name': 'Test incremental',
+                    'code': 'test',
+                    'prefix': '',
+                    'suffix': '',
+                    'type': 'incremental',
+                    })
+            self.assertEqual(self.sequence.get_id(sequence), '1')
 
-            self.sequence.write(sequence_id, {
-                'number_increment': 10,
-                })
-            self.assertEqual(self.sequence.get_id(sequence_id), '2')
-            self.assertEqual(self.sequence.get_id(sequence_id), '12')
+            self.sequence.write([sequence], {
+                    'number_increment': 10,
+                    })
+            self.assertEqual(self.sequence.get_id([sequence]), '2')
+            self.assertEqual(self.sequence.get_id([sequence]), '12')
 
-            self.sequence.write(sequence_id, {
-                'padding': 3,
-                })
-            self.assertEqual(self.sequence.get_id(sequence_id), '022')
+            self.sequence.write([sequence], {
+                    'padding': 3,
+                    })
+            self.assertEqual(self.sequence.get_id([sequence]), '022')
 
             transaction.cursor.rollback()
 
@@ -52,24 +52,22 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            sequence_id = self.sequence.create({
-                'name': 'Test decimal timestamp',
-                'code': 'test',
-                'prefix': '',
-                'suffix': '',
-                'type': 'decimal timestamp',
-                })
-            timestamp = self.sequence.get_id(sequence_id)
-            sequence = self.sequence.read(sequence_id, ['last_timestamp'])
-            self.assertEqual(timestamp, str(sequence['last_timestamp']))
+            sequence = self.sequence.create({
+                    'name': 'Test decimal timestamp',
+                    'code': 'test',
+                    'prefix': '',
+                    'suffix': '',
+                    'type': 'decimal timestamp',
+                    })
+            timestamp = self.sequence.get_id(sequence)
+            self.assertEqual(timestamp, str(sequence.last_timestamp))
 
-            self.assertNotEqual(self.sequence.get_id(sequence_id), timestamp)
+            self.assertNotEqual(self.sequence.get_id(sequence), timestamp)
 
-            sequence = self.sequence.browse(sequence_id)
             next_timestamp = self.sequence._timestamp(sequence)
-            self.assertRaises(Exception, self.sequence.write, sequence_id, {
-                'last_timestamp': next_timestamp + 100,
-                })
+            self.assertRaises(Exception, self.sequence.write, [sequence], {
+                    'last_timestamp': next_timestamp + 100,
+                    })
 
             transaction.cursor.rollback()
 
@@ -79,25 +77,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            sequence_id = self.sequence.create({
-                'name': 'Test hexadecimal timestamp',
-                'code': 'test',
-                'prefix': '',
-                'suffix': '',
-                'type': 'hexadecimal timestamp',
-                })
-            timestamp = self.sequence.get_id(sequence_id)
-            sequence = self.sequence.read(sequence_id, ['last_timestamp'])
+            sequence = self.sequence.create({
+                    'name': 'Test hexadecimal timestamp',
+                    'code': 'test',
+                    'prefix': '',
+                    'suffix': '',
+                    'type': 'hexadecimal timestamp',
+                    })
+            timestamp = self.sequence.get_id(sequence)
             self.assertEqual(timestamp,
-                    hex(int(sequence['last_timestamp']))[2:].upper())
+                hex(int(sequence.last_timestamp))[2:].upper())
 
-            self.assertNotEqual(self.sequence.get_id(sequence_id), timestamp)
+            self.assertNotEqual(self.sequence.get_id(sequence), timestamp)
 
-            sequence = self.sequence.browse(sequence_id)
             next_timestamp = self.sequence._timestamp(sequence)
-            self.assertRaises(Exception, self.sequence.write, sequence_id, {
-                'last_timestamp': next_timestamp + 100,
-                })
+            self.assertRaises(Exception, self.sequence.write, [sequence], {
+                    'last_timestamp': next_timestamp + 100,
+                    })
 
             transaction.cursor.rollback()
 
@@ -106,23 +102,23 @@
         Test prefix/suffix
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            sequence_id = self.sequence.create({
-                'name': 'Test incremental',
-                'code': 'test',
-                'prefix': 'prefix/',
-                'suffix': '/suffix',
-                'type': 'incremental',
-                })
-            self.assertEqual(self.sequence.get_id(sequence_id),
-                    'prefix/1/suffix')
+            sequence = self.sequence.create({
+                    'name': 'Test incremental',
+                    'code': 'test',
+                    'prefix': 'prefix/',
+                    'suffix': '/suffix',
+                    'type': 'incremental',
+                    })
+            self.assertEqual(self.sequence.get_id(sequence),
+                'prefix/1/suffix')
 
-            self.sequence.write(sequence_id, {
-                'prefix': '${year}-${month}-${day}/',
-                'suffix': '/${day}.${month}.${year}',
-                })
+            self.sequence.write([sequence], {
+                    'prefix': '${year}-${month}-${day}/',
+                    'suffix': '/${day}.${month}.${year}',
+                    })
             with Transaction().set_context(date=datetime.date(2010, 8, 15)):
-                self.assertEqual(self.sequence.get_id(sequence_id),
-                        '2010-08-15/2/15.08.2010')
+                self.assertEqual(self.sequence.get_id(sequence),
+                    '2010-08-15/2/15.08.2010')
 
 
 def suite():
diff -r fb51f5b4aa4c trytond/tests/test_trigger.py
--- a/trytond/tests/test_trigger.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_trigger.py	Mon Sep 24 20:36:36 2012 +0200
@@ -49,18 +49,18 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
             values = {
                 'name': 'Test',
-                'model': model_id,
+                'model': model.id,
                 'on_time': True,
                 'condition': 'True',
-                'action_model': model_id,
+                'action_model': model.id,
                 'action_function': 'test',
-            }
+                }
             self.assert_(self.trigger.create(values))
 
             # on_exclusive
@@ -70,17 +70,17 @@
                     combination_values = values.copy()
                     for mode in combination:
                         combination_values['on_%s' % mode] = True
-                    self.failUnlessRaises(Exception, self.trigger.create,
-                            combination_values)
+                    self.assertRaises(Exception, self.trigger.create,
+                        combination_values)
 
             # check_condition
             condition_values = values.copy()
             condition_values['condition'] = '='
-            self.failUnlessRaises(Exception, self.trigger.create,
-                    condition_values)
+            self.assertRaises(Exception, self.trigger.create,
+                condition_values)
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
     def test0020on_create(self):
@@ -89,68 +89,68 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
-            trigger_id = self.trigger.create({
-                'name': 'Test',
-                'model': model_id,
-                'on_create': True,
-                'condition': 'True',
-                'action_model': model_id,
-                'action_function': 'trigger',
-                })
+            trigger = self.trigger.create({
+                    'name': 'Test',
+                    'model': model.id,
+                    'on_create': True,
+                    'condition': 'True',
+                    'action_model': model.id,
+                    'action_function': 'trigger',
+                    })
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Trigger with condition
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    })
 
             # Matching condition
-            triggered_id = self.triggered.create({
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            triggered = self.triggered.create({
+                    'name': 'Bar',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Non matching condition
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
-            self.assert_(TRIGGER_LOGS == [])
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [])
 
             # With limit number
-            self.trigger.write(trigger_id, {
-                'condition': 'True',
-                'limit_number': 1,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'condition': 'True',
+                    'limit_number': 1,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # With minimum delay
-            self.trigger.write(trigger_id, {
-                'limit_number': 0,
-                'minimum_delay': 1,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'limit_number': 0,
+                    'minimum_delay': 1,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
     def test0030on_write(self):
@@ -159,108 +159,108 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
-            trigger_id = self.trigger.create({
-                'name': 'Test',
-                'model': model_id,
-                'on_write': True,
-                'condition': 'True',
-                'action_model': model_id,
-                'action_function': 'trigger',
-                })
+            trigger = self.trigger.create({
+                    'name': 'Test',
+                    'model': model.id,
+                    'on_write': True,
+                    'condition': 'True',
+                    'action_model': model.id,
+                    'action_function': 'trigger',
+                    })
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
-            self.triggered.write(triggered_id, {
-                'name': 'Foo',
-                })
-            self.assert_(TRIGGER_LOGS == [])
+            self.triggered.write([triggered], {
+                    'name': 'Foo',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [])
 
             # Trigger with condition
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    })
 
             # Matching condition
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # No change in condition
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [])
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [])
 
             # Different change in condition
-            self.triggered.write(triggered_id, {
-                'name': 'Foo',
-                })
-            self.assert_(TRIGGER_LOGS == [])
+            self.triggered.write([triggered], {
+                    'name': 'Foo',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [])
 
             # With limit number
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                'limit_number': 1,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.triggered.write(triggered_id, {
-                'name': 'Foo',
-                })
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    'limit_number': 1,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
+            self.triggered.write([triggered], {
+                    'name': 'Foo',
+                    })
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # With minimum delay
-            self.trigger.write(trigger_id, {
-                'limit_number': 0,
-                'minimum_delay': MAXINT,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
+            self.trigger.write([trigger], {
+                    'limit_number': 0,
+                    'minimum_delay': MAXINT,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
             for name in ('Bar', 'Foo', 'Bar'):
-                self.triggered.write(triggered_id, {
-                    'name': name,
-                    })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+                self.triggered.write([triggered], {
+                        'name': name,
+                        })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
-            self.trigger.write(trigger_id, {
-                'minimum_delay': 0.02,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
+            self.trigger.write([trigger], {
+                    'minimum_delay': 0.02,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
             for name in ('Bar', 'Foo'):
-                self.triggered.write(triggered_id, {
-                    'name': name,
+                self.triggered.write([triggered], {
+                        'name': name,
+                        })
+            time.sleep(1.2)
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
                     })
-            time.sleep(1.2)
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id),
-                ([triggered_id], trigger_id)])
+            self.assertEqual(TRIGGER_LOGS,
+                [([triggered], trigger), ([triggered], trigger)])
             TRIGGER_LOGS.pop()
             TRIGGER_LOGS.pop()
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
     def test0040on_delete(self):
@@ -269,86 +269,86 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
-            trigger_id = self.trigger.create({
-                'name': 'Test',
-                'model': model_id,
-                'on_delete': True,
-                'condition': 'True',
-                'action_model': model_id,
-                'action_function': 'trigger',
-                })
+            trigger = self.trigger.create({
+                    'name': 'Test',
+                    'model': model.id,
+                    'on_delete': True,
+                    'condition': 'True',
+                    'action_model': model.id,
+                    'action_function': 'trigger',
+                    })
 
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
             # Trigger with condition
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    })
 
-            triggered_id = self.triggered.create({
-                'name': 'Bar',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Bar',
+                    })
 
             # Matching condition
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
 
             # Non matching condition
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [])
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [])
             Transaction().delete = {}
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
             # With limit number
-            self.trigger.write(trigger_id, {
-                'condition': 'True',
-                'limit_number': 1,
-                })
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'condition': 'True',
+                    'limit_number': 1,
+                    })
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
             # Delete trigger logs because SQLite reuse the same triggered_id
             self.trigger_log.delete(self.trigger_log.search([
-                        ('trigger', '=', trigger_id),
+                        ('trigger', '=', trigger.id),
                         ]))
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
             # With minimum delay
-            self.trigger.write(trigger_id, {
-                'limit_number': 0,
-                'minimum_delay': 1,
-                })
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'limit_number': 0,
+                    'minimum_delay': 1,
+                    })
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
     def test0050on_time(self):
@@ -357,91 +357,91 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
-            trigger_id = self.trigger.create({
-                'name': 'Test',
-                'model': model_id,
-                'on_time': True,
-                'condition': 'True',
-                'action_model': model_id,
-                'action_function': 'trigger',
-                })
+            trigger = self.trigger.create({
+                    'name': 'Test',
+                    'model': model.id,
+                    'on_time': True,
+                    'condition': 'True',
+                    'action_model': model.id,
+                    'action_function': 'trigger',
+                    })
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Trigger with condition
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    })
 
             # Matching condition
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Non matching condition
-            self.triggered.write(triggered_id, {
-                'name': 'Foo',
-                })
+            self.triggered.write([triggered], {
+                    'name': 'Foo',
+                    })
             self.trigger.trigger_time()
             self.assert_(TRIGGER_LOGS == [])
 
             # With limit number
-            self.trigger.write(trigger_id, {
-                'condition': 'True',
-                'limit_number': 1,
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'True',
+                    'limit_number': 1,
+                    })
             self.trigger.trigger_time()
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Delete trigger logs of limit number test
             self.trigger_log.delete(self.trigger_log.search([
-                        ('trigger', '=', trigger_id),
+                        ('trigger', '=', trigger.id),
                         ]))
 
             # With minimum delay
-            self.trigger.write(trigger_id, {
-                'limit_number': 0,
-                'minimum_delay': MAXINT,
-                })
+            self.trigger.write([trigger], {
+                    'limit_number': 0,
+                    'minimum_delay': MAXINT,
+                    })
             self.trigger.trigger_time()
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
             # Delete trigger logs of previous minimum delay test
             self.trigger_log.delete(self.trigger_log.search([
-                ('trigger', '=', trigger_id),
-                ]))
+                        ('trigger', '=', trigger.id),
+                        ]))
 
-            self.trigger.write(trigger_id, {
-                'minimum_delay': 0.02,
-                })
+            self.trigger.write([trigger], {
+                    'minimum_delay': 0.02,
+                    })
             self.trigger.trigger_time()
             time.sleep(1.2)
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id),
-                ([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger),
+                    ([triggered], trigger)])
             TRIGGER_LOGS.pop()
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
 
diff -r fb51f5b4aa4c trytond/tests/test_tryton.py
--- a/trytond/tests/test_tryton.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_tryton.py	Mon Sep 24 20:36:36 2012 +0200
@@ -41,14 +41,13 @@
 if not CONFIG['admin_passwd']:
     CONFIG['admin_passwd'] = 'admin'
 
-from trytond.modules import register_classes
 from trytond.pool import Pool
 from trytond.backend import Database
 from trytond.protocols.dispatcher import create
 from trytond.transaction import Transaction
 from trytond.pyson import PYSONEncoder, Eval
 
-register_classes()
+Pool.start()
 
 if CONFIG['db_type'] == 'sqlite':
     DB_NAME = ':memory:'
@@ -121,27 +120,29 @@
         create(DB_NAME, CONFIG['admin_passwd'], 'en_US', USER_PASSWORD)
     with Transaction().start(DB_NAME, USER,
             context=CONTEXT) as transaction:
-        module_obj = POOL.get('ir.module.module')
+        Module = POOL.get('ir.module.module')
 
-        module_ids = module_obj.search([
-            ('name', '=', name),
-            ])
-        assert module_ids
+        modules = Module.search([
+                ('name', '=', name),
+                ])
+        assert modules
 
-        module_ids = module_obj.search([
-            ('name', '=', name),
-            ('state', '!=', 'installed'),
-            ])
+        modules = Module.search([
+                ('name', '=', name),
+                ('state', '!=', 'installed'),
+                ])
 
-        if not module_ids:
+        if not modules:
             return
 
-        module_obj.install(module_ids)
+        Module.install(modules)
         transaction.cursor.commit()
 
-        install_upgrade_obj = POOL.get('ir.module.module.install_upgrade',
-                type='wizard')
-        install_upgrade_obj.transition_upgrade(None)
+        InstallUpgrade = POOL.get('ir.module.module.install_upgrade',
+            type='wizard')
+        instance_id, _, _ = InstallUpgrade.create()
+        InstallUpgrade(instance_id).transition_upgrade()
+        InstallUpgrade.delete(instance_id)
         transaction.cursor.commit()
 
 
@@ -151,17 +152,17 @@
     '''
     with Transaction().start(DB_NAME, USER,
             context=CONTEXT) as transaction:
-        view_obj = POOL.get('ir.ui.view')
-        view_ids = view_obj.search([
-            ('module', '=', module_name),
-            ('model', '!=', ''),
-            ])
-        assert view_ids, "No views for %s" % module_name
-        for view in view_obj.browse(view_ids):
+        View = POOL.get('ir.ui.view')
+        views = View.search([
+                ('module', '=', module_name),
+                ('model', '!=', ''),
+                ])
+        assert views, "No views for %s" % module_name
+        for view in views:
             view_id = view.inherit and view.inherit.id or view.id
             model = view.model
-            model_obj = POOL.get(model)
-            res = model_obj.fields_view_get(view_id)
+            Model = POOL.get(model)
+            res = Model.fields_view_get(view_id)
             assert res['model'] == model
             tree = etree.fromstring(res['arch'])
             tree_root = tree.getroottree().getroot()
@@ -195,7 +196,7 @@
 
     with Transaction().start(DB_NAME, USER, context=CONTEXT):
         for mname, model in Pool().iterobject():
-            for fname, field in model._columns.iteritems():
+            for fname, field in model._fields.iteritems():
                 encoder = Encoder()
                 encoder.encode(field.domain)
                 if hasattr(field, 'digits'):
diff -r fb51f5b4aa4c trytond/tests/test_wizard.py
--- a/trytond/tests/test_wizard.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_wizard.py	Mon Sep 24 20:36:36 2012 +0200
@@ -4,7 +4,6 @@
 from trytond.tests.test_tryton import (POOL, DB_NAME, USER, CONTEXT,
     install_module)
 from trytond.transaction import Transaction
-from trytond.wizard import Session
 
 
 class WizardTestCase(unittest.TestCase):
@@ -13,6 +12,7 @@
         install_module('test')
         self.wizard = POOL.get('test.test_wizard', type='wizard')
         self.session = POOL.get('ir.session.wizard')
+        self.group = POOL.get('res.group')
 
     def test0010create(self):
         'Create Session Wizard'
@@ -32,17 +32,27 @@
         'Session Wizard'
         with Transaction().start(DB_NAME, USER, CONTEXT):
             session_id = self.session.create({})
-            session = Session(self.wizard, session_id)
-            self.assertEqual(session.start.name, None)
+            session = self.wizard(session_id)
+            self.assertEqual(session.start.id, None)
+            self.assertRaises(AttributeError, getattr, session.start, 'name')
+            self.assertEqual(hasattr(session.start, 'name'), False)
             session.start.name = 'Test'
-            self.assertEqual(session.start.user.id, None)
+            self.assertRaises(AttributeError, getattr, session.start, 'user')
+            self.assertEqual(hasattr(session.start, 'user'), False)
             session.start.user = USER
+            group_a = self.group.create({
+                    'name': 'Group A',
+                    })
+            group_b = self.group.create({
+                    'name': 'Group B',
+                    })
             session.start.groups = [
-                {'name': 'Group A'},
-                {'name': 'Group B'},
+                group_a,
+                group_b,
                 ]
-            session.save()
-            session = Session(self.wizard, session_id)
+            session._save()
+            session = self.wizard(session_id)
+            self.assertEqual(session.start.id, None)
             self.assertEqual(session.start.name, 'Test')
             self.assertEqual(session.start.user.id, USER)
             self.assertEqual(session.start.user.login, 'admin')
diff -r fb51f5b4aa4c trytond/tests/test_workflow.py
--- a/trytond/tests/test_workflow.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tests/test_workflow.py	Mon Sep 24 20:36:36 2012 +0200
@@ -12,23 +12,21 @@
 
     def setUp(self):
         install_module('test')
-        self.workflow_obj = POOL.get('test.workflowed')
+        self.workflow = POOL.get('test.workflowed')
 
     # TODO add test for Workflow.transition
     def test0010transition(self):
         'Test transition'
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            wkf_id = self.workflow_obj.create({})
+            wkf = self.workflow.create({})
 
-            self.workflow_obj.run([wkf_id])
-            self.assertEqual(self.workflow_obj.read(wkf_id)['state'],
-                'running')
+            self.workflow.run([wkf])
+            self.assertEqual(wkf.state, 'running')
 
-            self.workflow_obj.write(wkf_id, {
-                    'state': 'end',
-                    })
-            self.workflow_obj.run([wkf_id])
-            self.assertEqual(self.workflow_obj.read(wkf_id)['state'], 'end')
+            wkf.state = 'end'
+            wkf.save()
+            self.workflow.run([wkf])
+            self.assertEqual(wkf.state, 'end')
 
 
 def suite():
diff -r fb51f5b4aa4c trytond/tools/__init__.py
--- a/trytond/tools/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/tools/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -6,3 +6,8 @@
     from collections import OrderedDict
 except ImportError:
     from .ordereddict import OrderedDict
+
+
+class ClassProperty(property):
+    def __get__(self, cls, owner):
+        return self.fget.__get__(None, owner)()
diff -r fb51f5b4aa4c trytond/transaction.py
--- a/trytond/transaction.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/transaction.py	Mon Sep 24 20:36:36 2012 +0200
@@ -79,6 +79,7 @@
         self.delete_records = {}
         self.delete = {}
         self.timestamp = {}
+        self.counter = 0
         return _TransactionManager()
 
     def stop(self):
diff -r fb51f5b4aa4c trytond/url.py
--- a/trytond/url.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/url.py	Mon Sep 24 20:36:36 2012 +0200
@@ -8,10 +8,12 @@
 from trytond.config import CONFIG
 from trytond.transaction import Transaction
 
+__all__ = ['URLMixin']
 
-class URLMixin(object):
 
-    def get_url(self):
+class URLAccessor(object):
+
+    def __get__(self, inst, cls):
         from trytond.model import Model
         from trytond.wizard import Wizard
         from trytond.report import Report
@@ -22,17 +24,24 @@
             hostname.split('.'))
 
         url_part = {}
-        if isinstance(self, Model):
+        if issubclass(cls, Model):
             url_part['type'] = 'model'
-        elif isinstance(self, Wizard):
+        elif issubclass(cls, Wizard):
             url_part['type'] = 'wizard'
-        elif isinstance(self, Report):
+        elif issubclass(cls, Report):
             url_part['type'] = 'report'
         else:
             raise NotImplementedError
 
-        url_part['name'] = self._name
+        url_part['name'] = cls.__name__
         url_part['database'] = Transaction().cursor.database_name
 
         local_part = urllib.quote('%(database)s/%(type)s/%(name)s' % url_part)
+        if isinstance(inst, Model) and inst.id:
+            local_part += '/%d' % inst.id
         return 'tryton://%s/%s' % (hostname, local_part)
+
+
+class URLMixin(object):
+
+        __url__ = URLAccessor()
diff -r fb51f5b4aa4c trytond/webdav/__init__.py
--- a/trytond/webdav/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/webdav/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,3 +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 ..pool import Pool
 from .webdav import *
+
+
+def register():
+    Pool.register(
+        Collection,
+        Share,
+        Attachment,
+        module='webdav', type_='model')
diff -r fb51f5b4aa4c trytond/webdav/webdav.py
--- a/trytond/webdav/webdav.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/webdav/webdav.py	Mon Sep 24 20:36:36 2012 +0200
@@ -16,6 +16,10 @@
 from trytond.config import CONFIG
 from trytond.pyson import Eval
 
+__all__ = [
+    'Collection', 'Share', 'Attachment',
+    ]
+
 
 def get_webdav_url():
     if CONFIG['ssl_webdav']:
@@ -32,8 +36,7 @@
 
 class Collection(ModelSQL, ModelView):
     "Collection"
-    _name = "webdav.collection"
-    _description = __doc__
+    __name__ = "webdav.collection"
     name = fields.Char('Name', required=True, select=True)
     parent = fields.Many2One('webdav.collection', 'Parent',
        ondelete='RESTRICT', domain=[('model', '=', None)])
@@ -43,66 +46,61 @@
     complete_name = fields.Function(fields.Char('Complete Name',
             order_field='name'), 'get_rec_name')
 
-    def __init__(self):
-        super(Collection, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Collection, cls).__setup__()
+        cls._sql_constraints += [
             ('name_parent_uniq', 'UNIQUE (name, parent)',
                 'The collection name must be unique inside a collection!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_recursion', 'recursive_collections'),
             ('check_attachment', 'collection_file_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_collections': 'You can not create recursive ' \
                     'collections!',
             'collection_file_name': 'You can not create a collection\n' \
                     'in a collection with the name of an ' \
                     'existing file!',
         })
-        self.ext2mime = {
+        cls.ext2mime = {
             '.png': 'image/png',
             '.odt': 'application/vnd.oasis.opendocument.text',
             '.pdf': 'application/pdf',
         }
 
-    def default_domain(self):
+    @staticmethod
+    def default_domain():
         return '[]'
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        names = {}
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.rec_name + '/' + self.name
+        else:
+            return self.name
 
-        def _name(collection):
-            if collection.id in names:
-                return names[collection.id]
-            elif collection.parent:
-                return _name(collection.parent) + '/' + collection.name
-            else:
-                return collection.name
-        for collection in self.browse(ids):
-            names[collection.id] = _name(collection)
-        return names
-
-    def check_attachment(self, ids):
+    @classmethod
+    def check_attachment(cls, collections):
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
-        for collection in self.browse(ids):
+        Attachment = pool.get('ir.attachment')
+        for collection in collections:
             if collection.parent:
-                attachment_ids = attachment_obj.search([
+                attachments = Attachment.search([
                     ('resource', '=', '%s,%s' %
-                        (self._name, collection.parent.id)),
+                        (cls.__name__, collection.parent.id)),
                     ])
-                for attachment in attachment_obj.browse(attachment_ids):
+                for attachment in attachments:
                     if attachment.name == collection.name:
                         return False
         return True
 
-    def _uri2object(self, uri, object_name=_name, object_id=None, cache=None):
+    @classmethod
+    def _uri2object(cls, uri, object_name=__name__, object_id=None,
+            cache=None):
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
-        report_obj = pool.get('ir.action.report')
+        Attachment = pool.get('ir.attachment')
+        Report = pool.get('ir.action.report')
         cache_uri = uri
 
         if cache is not None:
@@ -112,10 +110,10 @@
 
         if not uri:
             if cache is not None:
-                cache['_uri2object'][cache_uri] = (self._name, None)
-            return self._name, None
+                cache['_uri2object'][cache_uri] = (cls.__name__, None)
+            return cls.__name__, None
         name, uri = (uri.split('/', 1) + [None])[0:2]
-        if object_name == self._name:
+        if object_name == cls.__name__:
             collection_ids = None
             if cache is not None:
                 cache.setdefault('_parent2collection_ids', {})
@@ -123,10 +121,9 @@
                     collection_ids = cache['_parent2collection_ids'][
                         object_id].get(name, [])
             if collection_ids is None:
-                collection_ids = self.search([
+                collections = cls.search([
                     ('parent', '=', object_id),
                     ])
-                collections = self.browse(collection_ids)
                 collection_ids = []
                 if cache is not None:
                     cache['_parent2collection_ids'].setdefault(object_id, {})
@@ -142,7 +139,7 @@
                                 collection.model.model
                         else:
                             cache['_collection_name'][collection.id] = \
-                                self._name
+                                cls.__name__
                     if collection.name == name:
                         collection_ids.append(collection.id)
             if collection_ids:
@@ -153,7 +150,7 @@
                     if object_id in cache['_collection_name']:
                         object_name2 = cache['_collection_name'][object_id]
                 if object_name2 is None:
-                    collection = self.browse(object_id)
+                    collection = cls(object_id)
                     if collection.model and uri:
                         object_name = collection.model.model
                         if cache is not None:
@@ -175,10 +172,9 @@
                             (object_name, object_id)].get(name, [])
                 attachment_id = None
                 if attachment_ids is None:
-                    attachment_ids = attachment_obj.search([
+                    attachments = Attachment.search([
                         ('resource', '=', '%s,%s' % (object_name, object_id)),
                         ])
-                    attachments = attachment_obj.browse(attachment_ids)
                     key = (object_name, object_id)
                     attachment_ids = []
                     if cache is not None:
@@ -219,10 +215,9 @@
                     if cache is not None:
                         cache['_uri2object'][cache_uri] = (None, 0)
                     return None, 0
-                report_ids = report_obj.search([
+                reports = Report.search([
                     ('model', '=', object_name),
                     ])
-                reports = report_obj.browse(report_ids)
                 for report in reports:
                     report_name = report.name + '-' + str(report.id) \
                             + '.' + report.extension
@@ -240,10 +235,9 @@
                         attachment_ids = cache['_model&id2attachment_ids'][
                             (object_name, object_id)].get(name, [])
                 if attachment_ids is None:
-                    attachment_ids = attachment_obj.search([
+                    attachments = Attachment.search([
                         ('resource', '=', '%s,%s' % (object_name, object_id)),
                         ])
-                    attachments = attachment_obj.browse(attachment_ids)
                     key = (object_name, object_id)
                     attachment_ids = []
                     if cache is not None:
@@ -266,7 +260,7 @@
                     cache['_uri2object'][cache_uri] = (object_name, object_id)
                 return object_name, object_id
         if uri:
-            res = self._uri2object(uri, object_name, object_id, cache=cache)
+            res = cls._uri2object(uri, object_name, object_id, cache=cache)
             if cache is not None:
                 cache['_uri2object'][cache_uri] = res
             return res
@@ -274,40 +268,41 @@
             cache['_uri2object'][cache_uri] = (object_name, object_id)
         return object_name, object_id
 
-    def get_childs(self, uri, filter=None, cache=None):
+    @classmethod
+    def get_childs(cls, uri, filter=None, cache=None):
         pool = Pool()
-        report_obj = pool.get('ir.action.report')
+        Report = pool.get('ir.action.report')
         res = []
         if filter:
             return []
         if not uri:
-            collection_ids = self.search([
+            collections = cls.search([
                 ('parent', '=', None),
                 ])
-            for collection in self.browse(collection_ids):
+            for collection in collections:
                 if '/' in collection.name:
                     continue
                 res.append(collection.name)
                 if cache is not None:
-                    cache.setdefault(self._name, {})
-                    cache[self._name][collection.id] = {}
+                    cache.setdefault(cls.__name__, {})
+                    cache[cls.__name__][collection.id] = {}
             return res
-        object_name, object_id = self._uri2object(uri, cache=cache)
-        if object_name == self._name and object_id:
-            collection = self.browse(object_id)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
+        if object_name == cls.__name__ and object_id:
+            collection = cls(object_id)
             if collection.model:
-                model_obj = pool.get(collection.model.model)
-                if not model_obj:
+                Model = pool.get(collection.model.model)
+                if not Model:
                     return res
-                model_ids = model_obj.search(
+                models = Model.search(
                         safe_eval(collection.domain or "[]"))
-                for child in model_obj.browse(model_ids):
+                for child in models:
                     if '/' in child.rec_name:
                         continue
                     res.append(child.rec_name + '-' + str(child.id))
                     if cache is not None:
-                        cache.setdefault(model_obj._name, {})
-                        cache[model_obj._name][child.id] = {}
+                        cache.setdefault(Model.__name__, {})
+                        cache[Model.__name__][child.id] = {}
                 return res
             else:
                 for child in collection.childs:
@@ -315,13 +310,12 @@
                         continue
                     res.append(child.name)
                     if cache is not None:
-                        cache.setdefault(self._name, {})
-                        cache[self._name][child.id] = {}
+                        cache.setdefault(cls.__name__, {})
+                        cache[cls.__name__][child.id] = {}
         if object_name not in ('ir.attachment', 'ir.action.report'):
-            report_ids = report_obj.search([
+            reports = Report.search([
                 ('model', '=', object_name),
                 ])
-            reports = report_obj.browse(report_ids)
             for report in reports:
                 report_name = report.name + '-' + str(report.id) \
                         + '.' + report.extension
@@ -329,40 +323,43 @@
                     continue
                 res.append(report_name)
                 if cache is not None:
-                    cache.setdefault(report_obj._name, {})
-                    cache[report_obj._name][report.id] = {}
+                    cache.setdefault(Report.__name__, {})
+                    cache[Report.__name__][report.id] = {}
 
-            attachment_obj = pool.get('ir.attachment')
-            attachment_ids = attachment_obj.search([
-                ('resource', '=', '%s,%s' % (object_name, object_id)),
-                ])
-            for attachment in attachment_obj.browse(attachment_ids):
+            Attachment = pool.get('ir.attachment')
+            attachments = Attachment.search([
+                    ('resource', '=', '%s,%s' % (object_name, object_id)),
+                    ])
+            for attachment in attachments:
                 if attachment.name and not attachment.link:
                     if '/' in attachment.name:
                         continue
                     res.append(attachment.name)
                     if cache is not None:
-                        cache.setdefault(attachment_obj._name, {})
-                        cache[attachment_obj._name][attachment.id] = {}
+                        cache.setdefault(Attachment.__name__, {})
+                        cache[Attachment.__name__][attachment.id] = {}
         return res
 
-    def get_resourcetype(self, uri, cache=None):
+    @classmethod
+    def get_resourcetype(cls, uri, cache=None):
         from pywebdav.lib.constants import COLLECTION, OBJECT
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name in ('ir.attachment', 'ir.action.report'):
             return OBJECT
         return COLLECTION
 
-    def get_displayname(self, uri, cache=None):
-        object_name, object_id = self._uri2object(uri, cache=cache)
-        model_obj = Pool().get(object_name)
-        return model_obj.browse(object_id).rec_name
+    @classmethod
+    def get_displayname(cls, uri, cache=None):
+        object_name, object_id = cls._uri2object(uri, cache=cache)
+        Model = Pool().get(object_name)
+        return Model(object_id).rec_name
 
-    def get_contentlength(self, uri, cache=None):
+    @classmethod
+    def get_contentlength(cls, uri, cache=None):
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
+        Attachment = pool.get('ir.attachment')
 
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name == 'ir.attachment':
 
             if cache is not None:
@@ -375,7 +372,7 @@
             else:
                 ids = [object_id]
 
-            attachments = attachment_obj.browse(ids)
+            attachments = Attachment.browse(ids)
 
             res = '0'
             for attachment in attachments:
@@ -394,28 +391,30 @@
             return res
         return '0'
 
-    def get_contenttype(self, uri, cache=None):
-        object_name, object_id = self._uri2object(uri, cache=cache)
+    @classmethod
+    def get_contenttype(cls, uri, cache=None):
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name in ('ir.attachment', 'ir.action.report'):
             ext = os.path.splitext(uri)[1]
             if not ext:
                 return "application/octet-stream"
-            return self.ext2mime.get(ext, 'application/octet-stream')
+            return cls.ext2mime.get(ext, 'application/octet-stream')
         return "application/octet-stream"
 
-    def get_creationdate(self, uri, cache=None):
+    @classmethod
+    def get_creationdate(cls, uri, cache=None):
         pool = Pool()
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name == 'ir.attachment':
-            model_obj = pool.get(object_name)
+            Model = pool.get(object_name)
             if object_id:
                 if cache is not None:
-                    cache.setdefault(model_obj._name, {})
-                    ids = cache[model_obj._name].keys()
+                    cache.setdefault(Model.__name__, {})
+                    ids = cache[Model.__name__].keys()
                     if object_id not in ids:
                         ids.append(object_id)
-                    elif 'creationdate' in cache[model_obj._name][object_id]:
-                        return cache[model_obj._name][object_id][
+                    elif 'creationdate' in cache[Model.__name__][object_id]:
+                        return cache[Model.__name__][object_id][
                             'creationdate']
                 else:
                     ids = [object_id]
@@ -426,32 +425,33 @@
                     red_sql, red_ids = reduce_ids('id', sub_ids)
                     cursor.execute('SELECT id, '
                         'EXTRACT(epoch FROM create_date) '
-                        'FROM "' + model_obj._table + '" '
+                        'FROM "' + Model._table + '" '
                         'WHERE ' + red_sql, red_ids)
                     for object_id2, date in cursor.fetchall():
                         if object_id2 == object_id:
                             res = date
                         if cache is not None:
-                            cache[model_obj._name].setdefault(object_id2, {})
-                            cache[model_obj._name][object_id2][
+                            cache[Model.__name__].setdefault(object_id2, {})
+                            cache[Model.__name__][object_id2][
                                 'creationdate'] = date
                 if res is not None:
                     return res
         return time.time()
 
-    def get_lastmodified(self, uri, cache=None):
+    @classmethod
+    def get_lastmodified(cls, uri, cache=None):
         pool = Pool()
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name == 'ir.attachment':
-            model_obj = pool.get(object_name)
+            Model = pool.get(object_name)
             if object_id:
                 if cache is not None:
-                    cache.setdefault(model_obj._name, {})
-                    ids = cache[model_obj._name].keys()
+                    cache.setdefault(Model.__name__, {})
+                    ids = cache[Model.__name__].keys()
                     if object_id not in ids:
                         ids.append(object_id)
-                    elif 'lastmodified' in cache[model_obj._name][object_id]:
-                        return cache[model_obj._name][object_id][
+                    elif 'lastmodified' in cache[Model.__name__][object_id]:
+                        return cache[Model.__name__][object_id][
                             'lastmodified']
                 else:
                     ids = [object_id]
@@ -463,27 +463,28 @@
                     cursor.execute('SELECT id, '
                         'EXTRACT(epoch FROM '
                             'COALESCE(write_date, create_date)) '
-                        'FROM "' + model_obj._table + '" '
+                        'FROM "' + Model._table + '" '
                         'WHERE ' + red_sql, red_ids)
                     for object_id2, date in cursor.fetchall():
                         if object_id2 == object_id:
                             res = date
                         if cache is not None:
-                            cache[model_obj._name].setdefault(object_id2, {})
-                            cache[model_obj._name][object_id2][
+                            cache[Model.__name__].setdefault(object_id2, {})
+                            cache[Model.__name__][object_id2][
                                 'lastmodified'] = date
                 if res is not None:
                     return res
         return time.time()
 
-    def get_data(self, uri, cache=None):
+    @classmethod
+    def get_data(cls, uri, cache=None):
         from pywebdav.lib.errors import DAV_NotFound
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
-        report_obj = pool.get('ir.action.report')
+        Attachment = pool.get('ir.attachment')
+        Report = pool.get('ir.action.report')
 
         if uri:
-            object_name, object_id = self._uri2object(uri, cache=cache)
+            object_name, object_id = cls._uri2object(uri, cache=cache)
 
             if object_name == 'ir.attachment' and object_id:
                 if cache is not None:
@@ -498,7 +499,7 @@
                         return res
                 else:
                     ids = [object_id]
-                attachments = attachment_obj.browse(ids)
+                attachments = Attachment.browse(ids)
 
                 res = DAV_NotFound
                 for attachment in attachments:
@@ -520,31 +521,32 @@
             if object_name == 'ir.action.report' and object_id:
                 report_id = int(uri.rsplit('/', 1)[-1].rsplit('-',
                     1)[-1].rsplit('.', 1)[0])
-                report = report_obj.browse(report_id)
+                report = Report(report_id)
                 if report.report_name:
-                    report_obj = pool.get(report.report_name,
+                    Report = pool.get(report.report_name,
                             type='report')
-                    val = report_obj.execute([object_id],
-                            {'id': object_id, 'ids': [object_id]})
+                    val = Report.execute([object_id],
+                        {'id': object_id, 'ids': [object_id]})
                     return val[1]
         raise DAV_NotFound
 
-    def put(self, uri, data, content_type, cache=None):
+    @classmethod
+    def put(cls, uri, data, content_type, cache=None):
         from pywebdav.lib.errors import DAV_Forbidden
         from pywebdav.lib.utils import get_uriparentpath, get_urifilename
-        object_name, object_id = self._uri2object(get_uriparentpath(uri),
+        object_name, object_id = cls._uri2object(get_uriparentpath(uri),
                 cache=cache)
         if not object_name \
                 or object_name in ('ir.attachment') \
                 or not object_id:
             raise DAV_Forbidden
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
-        object_name2, object_id2 = self._uri2object(uri, cache=cache)
+        Attachment = pool.get('ir.attachment')
+        object_name2, object_id2 = cls._uri2object(uri, cache=cache)
         if not object_id2:
             name = get_urifilename(uri)
             try:
-                attachment_obj.create({
+                Attachment.create({
                     'name': name,
                     'data': data,
                     'resource': '%s,%s' % (object_name, object_id),
@@ -553,25 +555,26 @@
                 raise DAV_Forbidden
         else:
             try:
-                attachment_obj.write(object_id2, {
+                Attachment.write(object_id2, {
                     'data': data,
                     })
             except Exception:
                 raise DAV_Forbidden
         return
 
-    def mkcol(self, uri, cache=None):
+    @classmethod
+    def mkcol(cls, uri, cache=None):
         from pywebdav.lib.errors import DAV_Forbidden
         from pywebdav.lib.utils import get_uriparentpath, get_urifilename
         if uri[-1:] == '/':
             uri = uri[:-1]
-        object_name, object_id = self._uri2object(get_uriparentpath(uri),
+        object_name, object_id = cls._uri2object(get_uriparentpath(uri),
                 cache=cache)
         if object_name != 'webdav.collection':
             raise DAV_Forbidden
         name = get_urifilename(uri)
         try:
-            self.create({
+            cls.create({
                 'name': name,
                 'parent': object_id,
                 })
@@ -579,50 +582,51 @@
             raise DAV_Forbidden
         return 201
 
-    def rmcol(self, uri, cache=None):
+    @classmethod
+    def rmcol(cls, uri, cache=None):
         from pywebdav.errors import DAV_Forbidden
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name != 'webdav.collection' \
                 or not object_id:
             raise DAV_Forbidden
         try:
-            self.delete(object_id)
+            cls.delete(object_id)
         except Exception:
             raise DAV_Forbidden
         return 200
 
-    def rm(self, uri, cache=None):
+    @classmethod
+    def rm(cls, uri, cache=None):
         from pywebdav.errors import DAV_Forbidden
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if not object_name:
             raise DAV_Forbidden
         if object_name != 'ir.attachment' \
                 or not object_id:
             raise DAV_Forbidden
         pool = Pool()
-        model_obj = pool.get(object_name)
+        Model = pool.get(object_name)
         try:
-            model_obj.delete(object_id)
+            Model.delete(object_id)
         except Exception:
             raise DAV_Forbidden
         return 200
 
-    def exists(self, uri, cache=None):
-        object_name, object_id = self._uri2object(uri, cache=cache)
+    @classmethod
+    def exists(cls, uri, cache=None):
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name and object_id:
             return 1
         return None
 
-    def current_user_privilege_set(self, uri, cache=None):
+    @staticmethod
+    def current_user_privilege_set(uri, cache=None):
         return ['create', 'read', 'write', 'delete']
 
-Collection()
-
 
 class Share(ModelSQL, ModelView):
     "Share"
-    _name = 'webdav.share'
-    _description = __doc__
+    __name__ = 'webdav.share'
     _rec_name = 'key'
 
     path = fields.Char('Path', required=True, select=True)
@@ -635,50 +639,49 @@
     note = fields.Text('Note')
     url = fields.Function(fields.Char('URL'), 'get_url')
 
-    def default_key(self):
+    @staticmethod
+    def default_key():
         return uuid.uuid4().hex
 
-    def default_user(self):
+    @staticmethod
+    def default_user():
         return Transaction().user
 
-    def default_expiration_date(self):
+    @staticmethod
+    def default_expiration_date():
         return datetime.date.today() + relativedelta(months=1)
 
-    def get_url(self, ids, name):
-        urls = {}
-        webdav_url = get_webdav_url()
-        for share in self.browse(ids):
-            urls[share.id] = urlparse.urljoin(webdav_url,
-                urlparse.urlunsplit((None, None, urllib.quote(share.path),
-                        urllib.urlencode([('key', share.key)]), None)))
-        return urls
+    def get_url(self, name):
+        return urlparse.urljoin(get_webdav_url(),
+            urlparse.urlunsplit((None, None, urllib.quote(self.path),
+                    urllib.urlencode([('key', self.key)]), None)))
 
-    def match(self, share, command, path):
+    @staticmethod
+    def match(share, command, path):
         "Test if share match with command and path"
         today = datetime.date.today()
         return (path.startswith(share.path)
             and share.expiration_date > today
             and command == 'GET')
 
-    def get_login(self, key, command, path):
+    @classmethod
+    def get_login(cls, key, command, path):
         """Validate the key for the command and path
         Return the user id if succeed or None
         """
-        share_ids = self.search([
+        shares = cls.search([
                 ('key', '=', key),
                 ])
-        if not share_ids:
+        if not shares:
             return None
-        for share in self.browse(share_ids):
-            if self.match(share, command, path):
+        for share in shares:
+            if cls.match(share, command, path):
                 return share.user.id
         return None
 
-Share()
-
 
 class Attachment(ModelSQL, ModelView):
-    _name = 'ir.attachment'
+    __name__ = 'ir.attachment'
 
     path = fields.Function(fields.Char('Path'), 'get_path')
     url = fields.Function(fields.Char('URL'), 'get_url')
@@ -688,36 +691,38 @@
                 ],
             depends=['path']), 'get_shares', 'set_shares')
 
-    def __init__(self):
-        super(Attachment, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Attachment, cls).__setup__()
+        cls._constraints += [
             ('check_collection', 'collection_attachment_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'collection_attachment_name': ('You can not create an attachment\n'
                     'in a collection with the name\n'
                     'of an existing child collection!'),
         })
 
-    def check_collection(self, ids):
+    @classmethod
+    def check_collection(cls, attachments):
         pool = Pool()
-        collection_obj = pool.get('webdav.collection')
-        for attachment in self.browse(ids):
+        Collection = pool.get('webdav.collection')
+        for attachment in attachments:
             if attachment.resource:
                 model_name, record_id = attachment.resource.split(',')
                 if model_name == 'webdav.collection':
-                    collection = collection_obj.browse(int(record_id))
+                    collection = Collection(int(record_id))
                     for child in collection.childs:
                         if child.name == attachment.name:
                             return False
         return True
 
-    def get_path(self, ids, name):
+    @classmethod
+    def get_path(cls, attachments, name):
         pool = Pool()
-        collection_obj = pool.get('webdav.collection')
-        paths = dict((x, None) for x in ids)
+        Collection = pool.get('webdav.collection')
+        paths = dict((a.id, None) for a in attachments)
 
-        attachments = self.browse(ids)
         resources = {}
         resource2attachments = {}
         for attachment in attachments:
@@ -728,17 +733,17 @@
             resources.setdefault(model_name, set()).add(record_id)
             resource2attachments.setdefault((model_name, record_id),
                 []).append(attachment)
-        collection_ids = collection_obj.search([
+        collections = Collection.search([
                 ('model.model', 'in', resources.keys()),
                 ])
-        for collection in collection_obj.browse(collection_ids):
+        for collection in collections:
             model_name = collection.model.model
-            model_obj = pool.get(model_name)
+            Model = pool.get(model_name)
             ids = list(resources[model_name])
             domain = safe_eval(collection.domain or '[]')
             domain = [domain, ('id', 'in', ids)]
-            record_ids = model_obj.search(domain)
-            for record in model_obj.browse(record_ids):
+            records = Model.search(domain)
+            for record in records:
                 for attachment in resource2attachments[
                         (model_name, record.id)]:
                     paths[attachment.id] = '/'.join((collection.rec_name,
@@ -746,59 +751,52 @@
                             attachment.name))
         if 'webdav.collection' in resources:
             collection_ids = list(resources['webdav.collection'])
-            for collection in collection_obj.browse(collection_ids):
+            for collection in Collection.browse(collection_ids):
                 for attachment in resource2attachments[
                         ('webdav.collection', collection.id)]:
                     paths[attachment.id] = '/'.join((collection.rec_name,
                             attachment.name))
         return paths
 
-    def get_url(self, ids, name):
-        webdav_url = get_webdav_url()
-        urls = {}
-        for attachment in self.browse(ids):
-            if attachment.path:
-                urls[attachment.id] = urlparse.urljoin(webdav_url,
-                    urllib.quote(attachment.path))
-            else:
-                urls[attachment.id] = None
-        return urls
+    def get_url(self, name):
+        if self.path:
+            return urlparse.urljoin(get_webdav_url(), urllib.quote(self.path))
 
-    def get_shares(self, ids, name):
-        share_obj = Pool().get('webdav.share')
-        shares = dict((x, []) for x in ids)
-        attachements = self.browse(ids)
-        path2attachement = dict((a.path, a) for a in attachements)
-        share_ids = share_obj.search([
+    @classmethod
+    def get_shares(cls, attachments, name):
+        Share = Pool().get('webdav.share')
+        shares = dict((a.id, []) for a in attachments)
+        path2attachement = dict((a.path, a) for a in attachments)
+        shares = Share.search([
                 ('path', 'in', path2attachement.keys()),
                 ])
-        for share in share_obj.browse(share_ids):
+        for share in shares:
             attachment = path2attachement[share.path]
             shares[attachment.id].append(share.id)
         return shares
 
-    def set_shares(self, ids, name, value):
-        share_obj = Pool().get('webdav.share')
+    @classmethod
+    def set_shares(cls, attachments, name, value):
+        Share = Pool().get('webdav.share')
 
         if not value:
             return
 
-        attachments = self.browse(ids)
         for action in value:
             if action[0] == 'create':
                 for attachment in attachments:
                     action[1]['path'] = attachment.path
-                    share_obj.create(action[1])
+                    Share.create(action[1])
             elif action[0] == 'write':
-                share_obj.write(action[1], action[2])
+                Share.write(action[1], action[2])
             elif action[0] == 'delete':
-                share_obj.delete(action[1])
+                Share.delete(Share.browse(action[1]))
             elif action[0] == 'delete_all':
                 paths = [a.path for a in attachments]
-                share_ids = share_obj.search([
+                shares = Share.search([
                         ('path', 'in', paths),
                         ])
-                share_obj.delete(share_ids)
+                Share.delete(shares)
             elif action[0] == 'unlink':
                 pass
             elif action[0] == 'add':
@@ -809,5 +807,3 @@
                 pass
             else:
                 raise Exception('Bad arguments')
-
-Attachment()
diff -r fb51f5b4aa4c trytond/wizard/__init__.py
--- a/trytond/wizard/__init__.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/wizard/__init__.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,3 +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.
-from .wizard import *
+from .wizard import Wizard, StateView, StateTransition, StateAction, Button
+
+__all__ = ['Wizard', 'StateView', 'StateTransition', 'StateAction', 'Button']
diff -r fb51f5b4aa4c trytond/wizard/wizard.py
--- a/trytond/wizard/wizard.py	Mon Sep 24 11:36:39 2012 +0200
+++ b/trytond/wizard/wizard.py	Mon Sep 24 20:36:36 2012 +0200
@@ -1,22 +1,22 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
-__all__ = ['Wizard', 'StateView', 'StateTransition', 'StateAction', 'Button',
-    'Session']
+__all__ = ['Wizard', 'StateView', 'StateTransition', 'StateAction', 'Button']
 
 try:
     import simplejson as json
 except ImportError:
     import json
+import copy
 
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 from trytond.error import WarningErrorMixin
 from trytond.url import URLMixin
 from trytond.protocols.jsonrpc import object_hook, JSONEncoder
 from trytond.model.fields import states_validate
 from trytond.pyson import PYSONEncoder
-from trytond.model.browse import BrowseRecordNull
+from trytond.rpc import RPC
 
 
 class Button(object):
@@ -46,6 +46,7 @@
     '''
     A State of a wizard.
     '''
+    name = None
 
 
 class StateView(State):
@@ -69,34 +70,34 @@
         '''
         Returns the view definition
         '''
-        model_obj = Pool().get(self.model_name)
-        model_data_obj = Pool().get('ir.model.data')
+        Model_ = Pool().get(self.model_name)
+        ModelData = Pool().get('ir.model.data')
         module, fs_id = self.view.split('.')
-        view_id = model_data_obj.get_id(module, fs_id)
-        return model_obj.fields_view_get(view_id=view_id, view_type='form')
+        view_id = ModelData.get_id(module, fs_id)
+        return Model_.fields_view_get(view_id=view_id, view_type='form')
 
-    def get_defaults(self, wizard, session, state_name, fields):
+    def get_defaults(self, wizard, state_name, fields):
         '''
         Returns defaults values for the fields
         '''
-        model_obj = Pool().get(self.model_name)
-        defaults = model_obj.default_get(fields)
+        Model_ = Pool().get(self.model_name)
+        defaults = Model_.default_get(fields)
         default = getattr(wizard, 'default_%s' % state_name, None)
         if default:
-            defaults.update(default(session, fields))
+            defaults.update(default(fields))
         return defaults
 
     def get_buttons(self, wizard, state_name):
         '''
         Returns button definitions translated
         '''
-        translation_obj = Pool().get('ir.translation')
+        Translation = Pool().get('ir.translation')
 
         def translation_key(button):
-            return (','.join((wizard._name, state_name, button.state)),
+            return (','.join((wizard.__name__, state_name, button.state)),
                 'wizard_button', Transaction().language, button.string)
         translation_keys = [translation_key(button) for button in self.buttons]
-        translations = translation_obj.get_sources(translation_keys)
+        translations = Translation.get_sources(translation_keys)
         encoder = PYSONEncoder()
         result = []
         for button in self.buttons:
@@ -132,131 +133,48 @@
     def get_action(self):
         "Returns action definition"
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        action_obj = pool.get('ir.action')
+        ModelData = pool.get('ir.model.data')
+        Action = pool.get('ir.action')
         module, fs_id = self.action_id.split('.')
-        action_id = action_obj.get_action_id(
-            model_data_obj.get_id(module, fs_id))
-        action = action_obj.browse(action_id)
-        return action_obj.get_action_values(action.type, action.id)
-
-
-class _SessionRecord(object):
-    '''
-    A record of a wizard form.
-    '''
-    # Declared in class to prevent:
-    # 'maximum recursion depth exceeded in __subclasscheck__'
-    _model = None
-    _data = None
-    __cache = None
-
-    def __init__(self, model, data):
-        self._model = model
-        self._data = data
-        self.__cache = {}
-        self.dirty = False
-
-    def __getattr__(self, name):
-        if name in self.__cache:
-            return self.__cache[name]
-        field = self._model._columns[name]
-        data = self._data.get(name, None)
-        target_obj = None
-        if hasattr(field, 'model_name'):
-            target_obj = Pool().get(field.model_name)
-        elif hasattr(field, 'get_target'):
-            target_obj = field.get_target()
-        if data:
-            if target_obj:
-                def instance(data):
-                    if isinstance(data, dict):
-                        return _SessionRecord(target_obj, data)
-                    return target_obj.browse(data)
-                if isinstance(data, list):
-                    data = [instance(x) for x in data]
-                else:
-                    data = instance(data)
-        elif target_obj and not isinstance(data, list):
-            return BrowseRecordNull()
-        self.__cache[name] = data
-        return data
-
-    def __setattr__(self, name, value):
-        if (self._model is not None
-                and (name in self._model._columns
-                    or name in self._model._inherit_fields)):
-            self.__cache.pop(name, None)
-            self._data[name] = value
-            self.dirty = True
-        else:
-            super(_SessionRecord, self).__setattr__(name, value)
-
-
-class Session(object):
-    '''
-    A wizard session.
-    '''
-
-    def __init__(self, wizard, session_id):
-        pool = Pool()
-        session_obj = pool.get('ir.session.wizard')
-        self._session = session_obj.browse(session_id)
-        self.data = json.loads(self._session.data.encode('utf-8'),
-            object_hook=object_hook)
-        for state_name, state in wizard.states.iteritems():
-            if isinstance(state, StateView):
-                model = pool.get(state.model_name)
-                self.data.setdefault(state_name, {})
-                setattr(self, state_name,
-                    _SessionRecord(model, self.data[state_name]))
-
-    @property
-    def dirty(self):
-        return any(getattr(self, state_name).dirty
-            for state_name in self.data)
-
-    def save(self):
-        "Save the session in database"
-        session_obj = Pool().get('ir.session.wizard')
-        if self.dirty:
-            session_obj.write(self._session.id, {
-                    'data': json.dumps(self.data, cls=JSONEncoder),
-                    })
+        action_id = Action.get_action_id(
+            ModelData.get_id(module, fs_id))
+        action = Action(action_id)
+        return Action.get_action_values(action.type, [action.id])[0]
 
 
 class Wizard(WarningErrorMixin, URLMixin):
-    _name = ""
+    __metaclass__ = PoolMeta
     start_state = 'start'
     end_state = 'end'
 
-    def __new__(cls):
-        Pool.register(cls, type='wizard')
+    @classmethod
+    def __setup__(cls):
+        cls.__rpc__ = {
+            'create': RPC(readonly=False),
+            'delete': RPC(readonly=False),
+            'execute': RPC(readonly=False),
+            }
+        cls._error_messages = {}
 
-    def __init__(self):
-        super(Wizard, self).__init__()
-        self._rpc = {
-            'create': True,
-            'delete': True,
-            'execute': True,
-        }
-        self._error_messages = {}
+        # Copy states
+        for attr in dir(cls):
+            if isinstance(getattr(cls, attr), State):
+                setattr(cls, attr, copy.deepcopy(getattr(cls, attr)))
 
-    @property
-    def states(self):
-        result = {}
-        for attr in dir(self):
-            if attr == 'states':
-                continue
-            if isinstance(getattr(self, attr), State):
-                result[attr] = getattr(self, attr)
-        return result
+    @classmethod
+    def __post_setup__(cls):
+        # Set states
+        cls.states = {}
+        for attr in dir(cls):
+            if isinstance(getattr(cls, attr), State):
+                cls.states[attr] = getattr(cls, attr)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
         cursor = Transaction().cursor
-        for state_name, state in self.states.iteritems():
+        for state_name, state in cls.states.iteritems():
             if isinstance(state, StateView):
                 for button in state.buttons:
                     cursor.execute('SELECT id, name, src '
@@ -266,16 +184,16 @@
                             'AND type = %s '
                             'AND name = %s',
                         (module_name, 'en_US', 'wizard_button',
-                            self._name + ',' + state_name + ',' +
+                            cls.__name__ + ',' + state_name + ',' +
                             button.state))
                     res = cursor.dictfetchall()
-                    src_md5 = translation_obj.get_src_md5(button.string)
+                    src_md5 = Translation.get_src_md5(button.string)
                     if not res:
                         cursor.execute('INSERT INTO ir_translation '
                             '(name, lang, type, src, src_md5, value, module, '
                                 'fuzzy) '
                             'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
-                            (self._name + ',' + state_name + ',' +
+                            (cls.__name__ + ',' + state_name + ',' +
                                 button.state,
                                 'en_US', 'wizard_button', button.string,
                                 src_md5, '', module_name, False))
@@ -289,31 +207,34 @@
             'WHERE lang = %s '
                 'AND type = %s '
                 'AND name = %s',
-            ('en_US', 'error', self._name))
+            ('en_US', 'error', cls.__name__))
         trans_error = {}
         for trans in cursor.dictfetchall():
             trans_error[trans['src']] = trans
 
-        for error in self._error_messages.values():
+        for error in cls._error_messages.values():
             if error not in trans_error:
-                error_md5 = translation_obj.get_src_md5(error)
+                error_md5 = Translation.get_src_md5(error)
                 cursor.execute('INSERT INTO ir_translation '
                     '(name, lang, type, src, src_md5,  value, module, fuzzy) '
                     'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
-                    (self._name, 'en_US', 'error', error, error_md5, '',
+                    (cls.__name__, 'en_US', 'error', error, error_md5, '',
                         module_name, False))
 
-    def create(self):
+    @classmethod
+    def create(cls):
         "Create a session"
-        session_obj = Pool().get('ir.session.wizard')
-        return (session_obj.create({}), self.start_state, self.end_state)
+        Session = Pool().get('ir.session.wizard')
+        return (Session.create({}).id, cls.start_state, cls.end_state)
 
-    def delete(self, session_id):
+    @classmethod
+    def delete(cls, session_id):
         "Delete the session"
-        session_obj = Pool().get('ir.session.wizard')
-        session_obj.delete(session_id)
+        Session = Pool().get('ir.session.wizard')
+        Session.delete([Session(session_id)])
 
-    def execute(self, session, data, state_name):
+    @classmethod
+    def execute(cls, session, data, state_name):
         '''
         Execute the wizard state.
 
@@ -328,22 +249,25 @@
                 - ``defaults``: a dictionary with default values
                 - ``buttons``: a list of buttons
         '''
+        wizard = cls(session)
+        for key, values in data.iteritems():
+            record = getattr(wizard, key)
+            for field, value in values.iteritems():
+                if field == 'id':
+                    continue
+                setattr(record, field, value)
+        return wizard._execute(state_name)
+
+    def _execute(self, state_name):
         if state_name == self.end_state:
             return {}
-        if isinstance(session, (int, long)):
-            session = Session(self, session)
-        for key, value in data.iteritems():
-            prev_data = session.data[key].copy()
-            session.data[key].update(value)
-            if prev_data != session.data[key]:
-                getattr(session, key).dirty = True
 
         state = self.states[state_name]
         result = {}
 
         if isinstance(state, StateView):
             view = state.get_view()
-            defaults = state.get_defaults(self, session, state_name,
+            defaults = state.get_defaults(self, state_name,
                 view['fields'].keys())
             buttons = state.get_buttons(self, state_name)
             result['view'] = {
@@ -358,13 +282,40 @@
                 action = state.get_action()
                 do = getattr(self, 'do_%s' % state_name, None)
                 if do:
-                    do_result = do(session, action)
+                    do_result = do(action)
                 else:
                     do_result = action, {}
             transition = getattr(self, 'transition_%s' % state_name, None)
             if transition:
-                result = self.execute(session, {}, transition(session))
+                result = self._execute(transition())
             if do_result:
                 result.setdefault('actions', []).append(do_result)
-        session.save()
+        self._save()
         return result
+
+    def __init__(self, session_id):
+        pool = Pool()
+        Session = pool.get('ir.session.wizard')
+        self._session_id = session_id
+        session = Session(session_id)
+        data = json.loads(session.data.encode('utf-8'),
+            object_hook=object_hook)
+        for state_name, state in self.states.iteritems():
+            if isinstance(state, StateView):
+                Target = pool.get(state.model_name)
+                data.setdefault(state_name, {})
+                setattr(self, state_name, Target(**data[state_name]))
+
+    def _save(self):
+        "Save the session in database"
+        Session = Pool().get('ir.session.wizard')
+        data = {}
+        for state_name, state in self.states.iteritems():
+            if isinstance(state, StateView):
+                data[state_name] = getattr(self, state_name)._default_values
+        session = Session(self._session_id)
+        data = json.dumps(data, cls=JSONEncoder)
+        if data != session.data.encode('utf-8'):
+            Session.write([session], {
+                    'data': data,
+                    })
diff -r f40675df5d4c trytond/modules/account/__init__.py
--- a/trytond/modules/account/__init__.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/__init__.py	Mon Sep 24 20:36:38 2012 +0200
@@ -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 trytond.pool import Pool
 from .fiscalyear import *
 from .account import *
 from .configuration import *
@@ -9,3 +10,93 @@
 from .move import *
 from .tax import *
 from .party import *
+
+
+def register():
+    Pool.register(
+        FiscalYear,
+        CloseFiscalYearStart,
+        TypeTemplate,
+        Type,
+        AccountTemplate,
+        Account,
+        AccountDeferral,
+        OpenChartAccountStart,
+        PrintGeneralLegderStart,
+        PrintTrialBalanceStart,
+        OpenBalanceSheetStart,
+        OpenIncomeStatementStart,
+        CreateChartStart,
+        CreateChartAccount,
+        CreateChartProperties,
+        UpdateChartStart,
+        UpdateChartSucceed,
+        OpenThirdPartyBalanceStart,
+        OpenAgedBalanceStart,
+        Configuration,
+        Period,
+        JournalType,
+        JournalView,
+        JournalViewColumn,
+        Journal,
+        JournalPeriod,
+        Move,
+        Reconciliation,
+        Line,
+        Move2,
+        OpenJournalAsk,
+        ReconcileLinesWriteOff,
+        UnreconcileLinesStart,
+        OpenReconcileLinesStart,
+        FiscalYearLine,
+        FiscalYear2,
+        PrintGeneralJournalStart,
+        TaxGroup,
+        TaxCodeTemplate,
+        TaxCode,
+        OpenChartTaxCodeStart,
+        TaxTemplate,
+        Tax,
+        TaxLine,
+        TaxRuleTemplate,
+        TaxRule,
+        TaxRuleLineTemplate,
+        TaxRuleLine,
+        AccountTemplateTaxTemplate,
+        AccountTemplate2,
+        AccountTax,
+        Account2,
+        Party,
+        module='account', type_='model')
+    Pool.register(
+        OpenType,
+        CloseFiscalYear,
+        OpenChartAccount,
+        PrintGeneralLegder,
+        PrintTrialBalance,
+        OpenBalanceSheet,
+        OpenIncomeStatement,
+        CreateChart,
+        UpdateChart,
+        OpenThirdPartyBalance,
+        OpenAgedBalance,
+        ClosePeriod,
+        ReOpenPeriod,
+        CloseJournalPeriod,
+        ReOpenJournalPeriod,
+        OpenJournal,
+        OpenAccount,
+        ReconcileLines,
+        UnreconcileLines,
+        OpenReconcileLines,
+        PrintGeneralJournal,
+        OpenChartTaxCode,
+        OpenTaxCode,
+        module='account', type_='wizard')
+    Pool.register(
+        GeneralLegder,
+        TrialBalance,
+        ThirdPartyBalance,
+        AgedBalance,
+        GeneralJournal,
+        module='account', type_='report')
diff -r f40675df5d4c trytond/modules/account/account.py
--- a/trytond/modules/account/account.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/account.py	Mon Sep 24 20:36:38 2012 +0200
@@ -15,11 +15,21 @@
 from trytond.config import CONFIG
 from trytond.backend import TableHandler
 
+__all__ = ['TypeTemplate', 'Type', 'OpenType', 'AccountTemplate', 'Account',
+    'AccountDeferral', 'OpenChartAccountStart', 'OpenChartAccount',
+    'PrintGeneralLegderStart', 'PrintGeneralLegder', 'GeneralLegder',
+    'PrintTrialBalanceStart', 'PrintTrialBalance', 'TrialBalance',
+    'OpenBalanceSheetStart', 'OpenBalanceSheet',
+    'OpenIncomeStatementStart', 'OpenIncomeStatement',
+    'CreateChartStart', 'CreateChartAccount', 'CreateChartProperties',
+    'CreateChart', 'UpdateChartStart', 'UpdateChartSucceed', 'UpdateChart',
+    'OpenThirdPartyBalanceStart', 'OpenThirdPartyBalance', 'ThirdPartyBalance',
+    'OpenAgedBalanceStart', 'OpenAgedBalance', 'AgedBalance']
+
 
 class TypeTemplate(ModelSQL, ModelView):
     'Account Type Template'
-    _name = 'account.account.type.template'
-    _description = __doc__
+    __name__ = 'account.account.type.template'
     name = fields.Char('Name', required=True, translate=True)
     parent = fields.Many2One('account.account.type.template', 'Parent',
             ondelete="RESTRICT")
@@ -35,137 +45,117 @@
         ('credit-debit', 'Credit - Debit'),
         ], 'Display Balance', required=True)
 
-    def __init__(self):
-        super(TypeTemplate, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(TypeTemplate, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_types'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_types': 'You can not create recursive types!',
         })
-        self._order.insert(0, ('sequence', 'ASC'))
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(TypeTemplate, self).init(module_name)
+        super(TypeTemplate, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_balance_sheet(self):
+    @staticmethod
+    def default_balance_sheet():
         return False
 
-    def default_income_statement(self):
+    @staticmethod
+    def default_income_statement():
         return False
 
-    def default_display_balance(self):
+    @staticmethod
+    def default_display_balance():
         return 'debit-credit'
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.get_rec_name(name) + '\\' + self.name
+        else:
+            return self.name
+
+    def _get_type_value(self, type=None):
+        '''
+        Set the values for account creation.
+        '''
         res = {}
-
-        def _name(type):
-            if type.parent:
-                return _name(type.parent) + '\\' + type.name
-            else:
-                return type.name
-        for type in self.browse(ids):
-            res[type.id] = _name(type)
+        if not type or type.name != self.name:
+            res['name'] = self.name
+        if not type or type.sequence != self.sequence:
+            res['sequence'] = self.sequence
+        if not type or type.balance_sheet != self.balance_sheet:
+            res['balance_sheet'] = self.balance_sheet
+        if not type or type.income_statement != self.income_statement:
+            res['income_statement'] = self.income_statement
+        if not type or type.display_balance != self.display_balance:
+            res['display_balance'] = self.display_balance
+        if not type or type.template.id != self.id:
+            res['template'] = self.id
         return res
 
-    def _get_type_value(self, template, type=None):
-        '''
-        Set the values for account creation.
-
-        :param template: the BrowseRecord of the template
-        :param type: the BrowseRecord of the type to update
-        :return: a dictionary with account fields as key and values as value
-        '''
-        res = {}
-        if not type or type.name != template.name:
-            res['name'] = template.name
-        if not type or type.sequence != template.sequence:
-            res['sequence'] = template.sequence
-        if not type or type.balance_sheet != template.balance_sheet:
-            res['balance_sheet'] = template.balance_sheet
-        if not type or type.income_statement != template.income_statement:
-            res['income_statement'] = template.income_statement
-        if not type or type.display_balance != template.display_balance:
-            res['display_balance'] = template.display_balance
-        if not type or type.template.id != template.id:
-            res['template'] = template.id
-        return res
-
-    def create_type(self, template, company_id, template2type=None,
-            parent_id=None):
+    def create_type(self, company_id, template2type=None, parent_id=None):
         '''
         Create recursively types based on template.
-
-        :param template: the template id or the BrowseRecord of the template
-                used for type creation
-        :param company_id: the id of the company for which types are created
-        :param template2type: a dictionary with template id as key
-                and type id as value, used to convert template id
-                into type. The dictionary is filled with new types
-        :param parent_id: the type id of the parent of the types that must
-                be created
-        :return: id of the type created
+        template2type is a dictionary with template id as key and type id as
+        value, used to convert template id into type. The dictionary is filled
+        with new types.
+        Return the id of the type created
         '''
-        type_obj = Pool().get('account.account.type')
-        lang_obj = Pool().get('ir.lang')
+        pool = Pool()
+        Type = pool.get('account.account.type')
+        Lang = pool.get('ir.lang')
 
         if template2type is None:
             template2type = {}
 
-        if isinstance(template, (int, long)):
-            template = self.browse(template)
-
-        if template.id not in template2type:
-            vals = self._get_type_value(template)
+        if self.id not in template2type:
+            vals = self._get_type_value()
             vals['company'] = company_id
             vals['parent'] = parent_id
 
-            new_id = type_obj.create(vals)
+            new_type = Type.create(vals)
 
-            prev_lang = template._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in template._columns.iteritems():
+            for field_name, field in self._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                template.setLang(lang)
-                data = {}
-                for field_name, field in template._columns.iteritems():
-                    if getattr(field, 'translate', False) \
-                            and template[field_name] != prev_data[field_name]:
-                        data[field_name] = template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        type_obj.write(new_id, data)
-            template.setLang(prev_lang)
-            template2type[template.id] = new_id
-        else:
-            new_id = template2type[template.id]
+                with Transaction().set_context(language=lang):
+                    template = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in template._fields.iteritems():
+                        if (getattr(field, 'translate', False)
+                                and (getattr(template, field_name) !=
+                                    prev_data[field_name])):
+                            data[field_name] = getattr(template, field_name)
+                    if data:
+                        Type.write([new_type], data)
+            template2type[self.id] = new_type.id
+        new_id = template2type[self.id]
 
         new_childs = []
-        for child in template.childs:
-            new_childs.append(self.create_type(child, company_id,
+        for child in self.childs:
+            new_childs.append(child.create_type(company_id,
                 template2type=template2type, parent_id=new_id))
         return new_id
 
-TypeTemplate()
-
 
 class Type(ModelSQL, ModelView):
     'Account Type'
-    _name = 'account.account.type'
-    _description = __doc__
+    __name__ = 'account.account.type'
     name = fields.Char('Name', size=None, required=True, translate=True)
     parent = fields.Many2One('account.account.type', 'Parent',
         ondelete="RESTRICT", domain=[
@@ -194,154 +184,139 @@
             ondelete="RESTRICT")
     template = fields.Many2One('account.account.type.template', 'Template')
 
-    def __init__(self):
-        super(Type, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Type, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_types'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_types': 'You can not create recursive types!',
         })
-        self._order.insert(0, ('sequence', 'ASC'))
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(Type, self).init(module_name)
+        super(Type, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_balance_sheet(self):
+    @staticmethod
+    def default_balance_sheet():
         return False
 
-    def default_income_statement(self):
+    @staticmethod
+    def default_income_statement():
         return False
 
-    def default_display_balance(self):
+    @staticmethod
+    def default_display_balance():
         return 'debit-credit'
 
-    def get_currency_digits(self, ids, name):
+    def get_currency_digits(self, name):
+        return self.company.currency.digits
+
+    @classmethod
+    def get_amount(cls, types, name):
+        pool = Pool()
+        Account = pool.get('account.account')
+
         res = {}
-        for type in self.browse(ids):
-            res[type.id] = type.company.currency.digits
+        for type_ in types:
+            res[type_.id] = Decimal('0.0')
+
+        childs = cls.search([
+                ('parent', 'child_of', [t.id for t in types]),
+                ])
+        type_sum = {}
+        for type_ in childs:
+            type_sum[type_.id] = Decimal('0.0')
+
+        accounts = Account.search([
+                ('type', 'in', [t.id for t in childs]),
+                ])
+        for account in accounts:
+            type_sum[account.type.id] += account.company.currency.round(
+                account.debit - account.credit)
+
+        for type_ in types:
+            childs = cls.search([
+                    ('parent', 'child_of', [type_.id]),
+                    ])
+            for child in childs:
+                res[type_.id] += type_sum[child.id]
+            res[type_.id] = type_.company.currency.round(res[type_.id])
+            if type_.display_balance == 'credit-debit':
+                res[type_.id] = - res[type_.id]
         return res
 
-    def get_amount(self, ids, name):
-        account_obj = Pool().get('account.account')
-        currency_obj = Pool().get('currency.currency')
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.get_rec_name(name) + '\\' + self.name
+        else:
+            return self.name
 
-        res = {}
-        for type_id in ids:
-            res[type_id] = Decimal('0.0')
+    @classmethod
+    def delete(cls, types):
+        types = cls.search([
+                ('parent', 'child_of', [t.id for t in types]),
+                ])
+        super(Type, cls).delete(types)
 
-        child_ids = self.search([
-            ('parent', 'child_of', ids),
-            ])
-        type_sum = {}
-        for type_id in child_ids:
-            type_sum[type_id] = Decimal('0.0')
-
-        account_ids = account_obj.search([
-            ('type', 'in', child_ids),
-            ])
-        for account in account_obj.browse(account_ids):
-            type_sum[account.type.id] += currency_obj.round(
-                    account.company.currency, account.debit - account.credit)
-
-        types = self.browse(ids)
-        for type in types:
-            child_ids = self.search([
-                ('parent', 'child_of', [type.id]),
-                ])
-            for child_id in child_ids:
-                res[type.id] += type_sum[child_id]
-            res[type.id] = currency_obj.round(type.company.currency,
-                    res[type.id])
-            if type.display_balance == 'credit-debit':
-                res[type.id] = - res[type.id]
-        return res
-
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-
-        def _name(type):
-            if type.parent:
-                return _name(type.parent) + '\\' + type.name
-            else:
-                return type.name
-        for type in self.browse(ids):
-            res[type.id] = _name(type)
-        return res
-
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        type_ids = self.search([
-            ('parent', 'child_of', ids),
-            ])
-        return super(Type, self).delete(type_ids)
-
-    def update_type(self, type, template2type=None):
+    def update_type(self, template2type=None):
         '''
         Update recursively types based on template.
-
-        :param type: a type id or the BrowseRecord of the type
-        :param template2type: a dictionary with template id as key
-                and type id as value, used to convert template id
-                into type. The dictionary is filled with new types
+        template2type is a dictionary with template id as key and type id as
+        value, used to convert template id into type. The dictionary is filled
+        with new types
         '''
-        template_obj = Pool().get('account.account.type.template')
-        lang_obj = Pool().get('ir.lang')
+        Lang = Pool().get('ir.lang')
 
         if template2type is None:
             template2type = {}
 
-        if isinstance(type, (int, long)):
-            type = self.browse(type)
+        if self.template:
+            vals = self.template._get_type_value(type=self)
+            if vals:
+                self.write([self], vals)
 
-        if type.template:
-            vals = template_obj._get_type_value(type.template, type=type)
-            if vals:
-                self.write(type.id, vals)
-
-            prev_lang = type._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in type.template._columns.iteritems():
+            for field_name, field in self.template._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = type.template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self.template, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                type.setLang(lang)
-                data = {}
-                for field_name, field in type.template._columns.iteritems():
-                    if (getattr(field, 'translate', False)
-                            and type.template[field_name] !=
-                            prev_data[field_name]):
-                        data[field_name] = type.template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        self.write(type.id, data)
-            type.setLang(prev_lang)
-            template2type[type.template.id] = type.id
+                with Transaction().set_context(language=lang):
+                    type_ = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in (
+                            type_.template._fields.iteritems()):
+                        if (getattr(field, 'translate', False)
+                                and (getattr(type_.template, field_name) !=
+                                    prev_data[field_name])):
+                            data[field_name] = getattr(type_.template,
+                                field_name)
+                    if data:
+                        self.write([type_], data)
+            template2type[self.template.id] = self.id
 
-        for child in type.childs:
-            self.update_type(child, template2type=template2type)
-
-Type()
+        for child in self.childs:
+            child.update_type(template2type=template2type)
 
 
 class OpenType(Wizard):
     'Open Type'
-    _name = 'account.account.open_type'
+    __name__ = 'account.account.open_type'
     start_state = 'open_'
     open_ = StateAction('account.act_account_list2')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         action['pyson_domain'] = PYSONEncoder().encode([
                 ('type', '=', Transaction().context['active_id']),
                 ])
@@ -351,14 +326,10 @@
                 })
         return action, {}
 
-OpenType()
-
 
 class AccountTemplate(ModelSQL, ModelView):
     'Account Template'
-    _name = 'account.account.template'
-    _description = __doc__
-
+    __name__ = 'account.account.template'
     name = fields.Char('Name', size=None, required=True, translate=True,
             select=True)
     code = fields.Char('Code', size=None, select=True)
@@ -388,89 +359,78 @@
             'invisible': Eval('kind') == 'view',
             }, depends=['kind'])
 
-    def __init__(self):
-        super(AccountTemplate, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(AccountTemplate, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_accounts'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_accounts': 'You can not create recursive accounts!',
         })
-        self._order.insert(0, ('code', 'ASC'))
-        self._order.insert(1, ('name', 'ASC'))
+        cls._order.insert(0, ('code', 'ASC'))
+        cls._order.insert(1, ('name', 'ASC'))
 
-    def default_kind(self):
+    @staticmethod
+    def default_kind():
         return 'view'
 
-    def default_reconcile(self):
+    @staticmethod
+    def default_reconcile():
         return False
 
-    def default_deferral(self):
+    @staticmethod
+    def default_deferral():
         return True
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
+    def get_rec_name(self, name):
+        if self.code:
+            return self.code + ' - ' + self.name
+        else:
+            return self.name
+
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        templates = cls.search([
+                ('code',) + clause[1:],
+                ], limit=1)
+        if templates:
+            return [('code',) + clause[1:]]
+        return [(cls._rec_name,) + clause[1:]]
+
+    def _get_account_value(self, account=None):
+        '''
+        Set the values for account creation.
+        '''
         res = {}
-        for template in self.browse(ids):
-            if template.code:
-                res[template.id] = template.code + ' - ' + template.name
-            else:
-                res[template.id] = template.name
+        if not account or account.name != self.name:
+            res['name'] = self.name
+        if not account or account.code != self.code:
+            res['code'] = self.code
+        if not account or account.kind != self.kind:
+            res['kind'] = self.kind
+        if not account or account.reconcile != self.reconcile:
+            res['reconcile'] = self.reconcile
+        if not account or account.deferral != self.deferral:
+            res['deferral'] = self.deferral
+        if not account or account.template.id != self.id:
+            res['template'] = self.id
         return res
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([
-            ('code',) + clause[1:],
-            ], limit=1)
-        if ids:
-            return [('code',) + clause[1:]]
-        return [(self._rec_name,) + clause[1:]]
-
-    def _get_account_value(self, template, account=None):
-        '''
-        Set the values for account creation.
-
-        :param template: the BrowseRecord of the template
-        :param account: the BrowseRecord of the account to update
-        :return: a dictionary with account fields as key and values as value
-        '''
-        res = {}
-        if not account or account.name != template.name:
-            res['name'] = template.name
-        if not account or account.code != template.code:
-            res['code'] = template.code
-        if not account or account.kind != template.kind:
-            res['kind'] = template.kind
-        if not account or account.reconcile != template.reconcile:
-            res['reconcile'] = template.reconcile
-        if not account or account.deferral != template.deferral:
-            res['deferral'] = template.deferral
-        if not account or account.template.id != template.id:
-            res['template'] = template.id
-        return res
-
-    def create_account(self, template, company_id, template2account=None,
+    def create_account(self, company_id, template2account=None,
             template2type=None, parent_id=None):
         '''
         Create recursively accounts based on template.
-
-        :param template: the template id or the BrowseRecord of template
-                used for account creation
-        :param company_id: the id of the company for which accounts
-                are created
-        :param template2account: a dictionary with template id as key
-                and account id as value, used to convert template id
-                into account. The dictionary is filled with new accounts
-        :param template2type: a dictionary with type template id as key
-                and type id as value, used to convert type template id
-                into type.
-        :param parent_id: the account id of the parent of the accounts
-                that must be created
-        :return: id of the account created
+        template2account is a dictionary with template id as key and account id
+        as value, used to convert template id into account. The dictionary is
+        filled with new accounts
+        template2type is a dictionary with type template id as key and type id
+        as value, used to convert type template id into type.
+        Return the id of the account created
         '''
-        account_obj = Pool().get('account.account')
-        lang_obj = Pool().get('ir.lang')
+        pool = Pool()
+        Account = pool.get('account.account')
+        Lang = pool.get('ir.lang')
 
         if template2account is None:
             template2account = {}
@@ -478,63 +438,54 @@
         if template2type is None:
             template2type = {}
 
-        if isinstance(template, (int, long)):
-            template = self.browse(template)
-
-        if template.id not in template2account:
-            vals = self._get_account_value(template)
+        if self.id not in template2account:
+            vals = self._get_account_value()
             vals['company'] = company_id
             vals['parent'] = parent_id
-            vals['type'] = template2type.get(template.type.id)
+            vals['type'] = template2type.get(self.type.id)
 
-            new_id = account_obj.create(vals)
+            new_account = Account.create(vals)
 
-            prev_lang = template._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in template._columns.iteritems():
+            for field_name, field in self._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                template.setLang(lang)
-                data = {}
-                for field_name, field in template._columns.iteritems():
-                    if getattr(field, 'translate', False) \
-                            and template[field_name] != prev_data[field_name]:
-                        data[field_name] = template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        account_obj.write(new_id, data)
-            template.setLang(prev_lang)
-            template2account[template.id] = new_id
-        else:
-            new_id = template2account[template.id]
+                with Transaction().set_context(language=lang):
+                    template = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in self._fields.iteritems():
+                        if (getattr(field, 'translate', False) \
+                                and (getattr(template, field_name) !=
+                                    prev_data[field_name])):
+                            data[field_name] = getattr(template, field_name)
+                    if data:
+                        Account.write([new_account], data)
+            template2account[self.id] = new_account.id
+        new_id = template2account[self.id]
 
         new_childs = []
-        for child in template.childs:
-            new_childs.append(self.create_account(child, company_id,
+        for child in self.childs:
+            new_childs.append(child.create_account(company_id,
                 template2account=template2account, template2type=template2type,
                 parent_id=new_id))
         return new_id
 
-    def update_account_taxes(self, template, template2account, template2tax,
+    def update_account_taxes(self, template2account, template2tax,
             template_done=None):
         '''
         Update recursively account taxes based on template.
-
-        :param template: the template id or the BrowseRecord of template
-            used for account creation
-        :param template2account: a dictionary with template id as key
-            and account id as value, used to convert template id into
-            account.
-        :param template2tax: a dictionary with tax template id as key
-            and tax id as value, used to convert tax template id into
-            tax.
-        :param template_done: a list of template id already updated.
-            The list is filled.
+        template2account is a dictionary with template id as key and account id
+        as value, used to convert template id into account.
+        template2tax is a dictionary with tax template id as key and tax id as
+        value, used to convert tax template id into tax.
+        template_done is a list of template id already updated. The list is
+        filled.
         '''
-        account_obj = Pool().get('account.account')
+        Account = Pool().get('account.account')
 
         if template2account is None:
             template2account = {}
@@ -543,29 +494,22 @@
         if template_done is None:
             template_done = []
 
-        if isinstance(template, (int, long)):
-            template = self.browse(template)
+        if self.id not in template_done:
+            if self.taxes:
+                Account.write([Account(template2account[self.id])], {
+                        'taxes': [
+                            ('add', template2tax[x.id]) for x in self.taxes],
+                        })
+            template_done.append(self.id)
 
-        if template.id not in template_done:
-            if template.taxes:
-                account_obj.write(template2account[template.id], {
-                    'taxes': [
-                        ('add', template2tax[x.id]) for x in template.taxes],
-                    })
-            template_done.append(template.id)
-
-        for child in template.childs:
-            self.update_account_taxes(child, template2account, template2tax,
-                    template_done=template_done)
-
-AccountTemplate()
+        for child in self.childs:
+            child.update_account_taxes(template2account, template2tax,
+                template_done=template_done)
 
 
 class Account(ModelSQL, ModelView):
     'Account'
-    _name = 'account.account'
-    _description = __doc__
-
+    __name__ = 'account.account'
     name = fields.Char('Name', size=None, required=True, translate=True,
             select=True)
     code = fields.Char('Code', size=None, select=True)
@@ -626,69 +570,72 @@
             }, depends=['kind'])
     template = fields.Many2One('account.account.template', 'Template')
 
-    def __init__(self):
-        super(Account, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Account, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_accounts'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_accounts': 'You can not create recursive accounts!',
             'delete_account_containing_move_lines': 'You can not delete ' \
                     'accounts containing move lines!',
         })
-        self._sql_error_messages.update({
+        cls._sql_error_messages.update({
             'parent_fkey': 'You can not delete accounts ' \
                     'that have children!',
         })
-        self._order.insert(0, ('code', 'ASC'))
-        self._order.insert(1, ('name', 'ASC'))
+        cls._order.insert(0, ('code', 'ASC'))
+        cls._order.insert(1, ('name', 'ASC'))
 
-    def default_left(self):
+    @staticmethod
+    def default_left():
         return 0
 
-    def default_right(self):
+    @staticmethod
+    def default_right():
         return 0
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company') or None
 
-    def default_reconcile(self):
+    @staticmethod
+    def default_reconcile():
         return False
 
-    def default_deferral(self):
+    @staticmethod
+    def default_deferral():
         return True
 
-    def default_kind(self):
+    @staticmethod
+    def default_kind():
         return 'view'
 
-    def get_currency(self, ids, name):
-        res = {}
-        for account in self.browse(ids):
-            res[account.id] = account.company.currency.id
-        return res
+    def get_currency(self, name):
+        return self.company.currency.id
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for account in self.browse(ids):
-            res[account.id] = account.company.currency.digits
-        return res
+    def get_currency_digits(self, name):
+        return self.company.currency.digits
 
-    def get_balance(self, ids, name):
+    @classmethod
+    def get_balance(cls, accounts, name):
         res = {}
         pool = Pool()
-        currency_obj = pool.get('currency.currency')
-        move_line_obj = pool.get('account.move.line')
-        fiscalyear_obj = pool.get('account.fiscalyear')
-        deferral_obj = pool.get('account.account.deferral')
+        Currency = pool.get('currency.currency')
+        MoveLine = pool.get('account.move.line')
+        FiscalYear = pool.get('account.fiscalyear')
+        Deferral = pool.get('account.account.deferral')
         cursor = Transaction().cursor
 
-        query_ids, args_ids = self.search([
-            ('parent', 'child_of', ids),
-            ], query_string=True)
-        line_query, fiscalyear_ids = move_line_obj.query_get()
+        query_ids, args_ids = cls.search([
+                ('parent', 'child_of', [a.id for a in accounts]),
+                ], query_string=True)
+        line_query, fiscalyear_ids = MoveLine.query_get()
         cursor.execute('SELECT a.id, ' \
                     'SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))) ' \
                 'FROM account_account a ' \
@@ -709,84 +656,79 @@
         account2company = {}
         id2company = {}
         id2account = {}
-        all_ids = self.search([('parent', 'child_of', ids)])
-        accounts = self.browse(all_ids)
-        for account in accounts:
+        all_accounts = cls.search([('parent', 'child_of',
+                    [a.id for a in accounts])])
+        for account in all_accounts:
             account2company[account.id] = account.company.id
             id2company[account.company.id] = account.company
             id2account[account.id] = account
 
-        for account_id in ids:
-            res.setdefault(account_id, Decimal('0.0'))
-            child_ids = self.search([
-                ('parent', 'child_of', [account_id]),
-                ])
-            company_id = account2company[account_id]
+        for account in accounts:
+            res.setdefault(account.id, Decimal('0.0'))
+            childs = cls.search([
+                    ('parent', 'child_of', [account.id]),
+                    ])
+            company_id = account2company[account.id]
             to_currency = id2company[company_id].currency
-            for child_id in child_ids:
-                child_company_id = account2company[child_id]
+            for child in childs:
+                child_company_id = account2company[child.id]
                 from_currency = id2company[child_company_id].currency
-                res[account_id] += currency_obj.compute(from_currency,
-                        account_sum.get(child_id, Decimal('0.0')), to_currency,
+                res[account.id] += Currency.compute(from_currency,
+                        account_sum.get(child.id, Decimal('0.0')), to_currency,
                         round=True)
 
         youngest_fiscalyear = None
-        for fiscalyear in fiscalyear_obj.browse(fiscalyear_ids):
+        for fiscalyear in FiscalYear.browse(fiscalyear_ids):
             if not youngest_fiscalyear \
                     or youngest_fiscalyear.start_date > fiscalyear.start_date:
                 youngest_fiscalyear = fiscalyear
 
         fiscalyear = None
         if youngest_fiscalyear:
-            fiscalyear_ids = fiscalyear_obj.search([
+            fiscalyears = FiscalYear.search([
                 ('end_date', '<=', youngest_fiscalyear.start_date),
                 ('company', '=', youngest_fiscalyear.company),
                 ], order=[('end_date', 'DESC')], limit=1)
-            if fiscalyear_ids:
-                fiscalyear = fiscalyear_obj.browse(fiscalyear_ids[0])
+            if fiscalyears:
+                fiscalyear = fiscalyears[0]
 
         if fiscalyear:
             if fiscalyear.state == 'close':
-                deferral_ids = deferral_obj.search([
+                deferrals = Deferral.search([
                     ('fiscalyear', '=', fiscalyear.id),
-                    ('account', 'in', ids),
+                    ('account', 'in', [a.id for a in accounts]),
                     ])
                 id2deferral = {}
-                for deferral in deferral_obj.browse(deferral_ids):
+                for deferral in deferrals:
                     id2deferral[deferral.account.id] = deferral
 
-                for account_id in ids:
-                    if account_id in id2deferral:
-                        deferral = id2deferral[account_id]
-                        res[account_id] += deferral.debit - deferral.credit
+                for account in accounts:
+                    if account.id in id2deferral:
+                        deferral = id2deferral[account.id]
+                        res[account.id] += deferral.debit - deferral.credit
             else:
                 with Transaction().set_context(fiscalyear=fiscalyear.id,
                         date=None, periods=None):
-                    res2 = self.get_balance(ids, name)
-                for account_id in ids:
-                    res[account_id] += res2[account_id]
+                    res2 = cls.get_balance(accounts, name)
+                for account in accounts:
+                    res[account.id] += res2[account.id]
 
-        for account_id in ids:
-            company_id = account2company[account_id]
+        for account in accounts:
+            company_id = account2company[account.id]
             to_currency = id2company[company_id].currency
-            res[account_id] = currency_obj.round(to_currency, res[account_id])
+            res[account.id] = to_currency.round(res[account.id])
         return res
 
-    def get_credit_debit(self, ids, names):
+    @classmethod
+    def get_credit_debit(cls, accounts, names):
         '''
-        Function to compute debit, credit for account ids.
-
-        :param ids: the ids of the account
-        :param names: the list of field name to compute
-        :return: a dictionary with all field names as key and
-            a dictionary as value with id as key
+        Function to compute debit, credit for accounts.
         '''
         res = {}
         pool = Pool()
-        move_line_obj = pool.get('account.move.line')
-        currency_obj = pool.get('currency.currency')
-        fiscalyear_obj = pool.get('account.fiscalyear')
-        deferral_obj = pool.get('account.account.deferral')
+        MoveLine = pool.get('account.move.line')
+        FiscalYear = pool.get('account.fiscalyear')
+        Deferral = pool.get('account.account.deferral')
         cursor = Transaction().cursor
 
         for name in names:
@@ -794,7 +736,8 @@
                 raise Exception('Bad argument')
             res[name] = {}
 
-        line_query, fiscalyear_ids = move_line_obj.query_get()
+        ids = [a.id for a in accounts]
+        line_query, fiscalyear_ids = MoveLine.query_get()
         for i in range(0, len(ids), cursor.IN_MAX):
             sub_ids = ids[i:i + cursor.IN_MAX]
             red_sql, red_ids = reduce_ids('a.id', sub_ids)
@@ -820,130 +763,120 @@
 
         account2company = {}
         id2company = {}
-        accounts = self.browse(ids)
         for account in accounts:
             account2company[account.id] = account.company.id
             id2company[account.company.id] = account.company
 
-        for account_id in ids:
+        for account in accounts:
             for name in names:
-                res[name].setdefault(account_id, Decimal('0.0'))
+                res[name].setdefault(account.id, Decimal('0.0'))
 
         youngest_fiscalyear = None
-        for fiscalyear in fiscalyear_obj.browse(fiscalyear_ids):
+        for fiscalyear in FiscalYear.browse(fiscalyear_ids):
             if not youngest_fiscalyear \
                     or youngest_fiscalyear.start_date > fiscalyear.start_date:
                 youngest_fiscalyear = fiscalyear
 
         fiscalyear = None
         if youngest_fiscalyear:
-            fiscalyear_ids = fiscalyear_obj.search([
+            fiscalyears = FiscalYear.search([
                 ('end_date', '<=', youngest_fiscalyear.start_date),
                 ('company', '=', youngest_fiscalyear.company),
                 ], order=[('end_date', 'DESC')], limit=1)
-            if fiscalyear_ids:
-                fiscalyear = fiscalyear_obj.browse(fiscalyear_ids[0])
+            if fiscalyears:
+                fiscalyear, = fiscalyears
 
         if fiscalyear:
             if fiscalyear.state == 'close':
-                deferral_ids = deferral_obj.search([
+                deferrals = Deferral.search([
                     ('fiscalyear', '=', fiscalyear.id),
                     ('account', 'in', ids),
                     ])
                 id2deferral = {}
-                for deferral in deferral_obj.browse(deferral_ids):
+                for deferral in deferrals:
                     id2deferral[deferral.account.id] = deferral
 
-                for account_id in ids:
-                    if account_id in id2deferral:
-                        deferral = id2deferral[account_id]
+                for account in accounts:
+                    if account.id in id2deferral:
+                        deferral = id2deferral[account.id]
                         for name in names:
-                            res[name][account_id] += deferral[name]
+                            res[name][account.id] += deferral[name]
             else:
                 with Transaction().set_context(fiscalyear=fiscalyear.id,
                         date=None, dates=None):
-                    res2 = self.get_credit_debit(ids, names)
-                for account_id in ids:
+                    res2 = cls.get_credit_debit(accounts, names)
+                for account in accounts:
                     for name in names:
-                        res[name][account_id] += res2[name][account_id]
+                        res[name][account.id] += res2[name][account.id]
 
-        for account_id in ids:
-            company_id = account2company[account_id]
+        for account in accounts:
+            company_id = account2company[account.id]
             currency = id2company[company_id].currency
             for name in names:
-                res[name][account_id] = currency_obj.round(currency,
-                        res[name][account_id])
+                res[name][account.id] = currency.round(res[name][account.id])
         return res
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for account in self.browse(ids):
-            if account.code:
-                res[account.id] = account.code + ' - ' + account.name
-            else:
-                res[account.id] = account.name
-        return res
+    def get_rec_name(self, name):
+        if self.code:
+            return self.code + ' - ' + self.name
+        else:
+            return self.name
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([
-            ('code',) + clause[1:],
-            ], limit=1)
-        if ids:
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        accounts = cls.search([
+                ('code',) + clause[1:],
+                ], limit=1)
+        if accounts:
             return [('code',) + clause[1:]]
-        return [(self._rec_name,) + clause[1:]]
+        return [(cls._rec_name,) + clause[1:]]
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, accounts, default=None):
         if default is None:
             default = {}
         default['left'] = 0
         default['right'] = 0
-        res = super(Account, self).copy(ids, default=default)
-        self._rebuild_tree('parent', None, 0)
-        return res
+        new_accounts = super(Account, cls).copy(accounts, default=default)
+        cls._rebuild_tree('parent', None, 0)
+        return new_accounts
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, accounts, vals):
         if not vals.get('active', True):
-            move_line_obj = Pool().get('account.move.line')
-            account_ids = self.search([
-                ('parent', 'child_of', ids),
-                ])
-            if move_line_obj.search([
-                ('account', 'in', account_ids),
-                ]):
+            MoveLine = Pool().get('account.move.line')
+            childs = cls.search([
+                    ('parent', 'child_of', [a.id for a in accounts]),
+                    ])
+            if MoveLine.search([
+                        ('account', 'in', [a.id for a in childs]),
+                        ]):
                 vals = vals.copy()
                 del vals['active']
-        return super(Account, self).write(ids, vals)
+        super(Account, cls).write(accounts, vals)
 
-    def delete(self, ids):
-        move_line_obj = Pool().get('account.move.line')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        account_ids = self.search([
-            ('parent', 'child_of', ids),
-            ])
-        if move_line_obj.search([
-            ('account', 'in', account_ids),
-            ]):
-            self.raise_user_error('delete_account_containing_move_lines')
-        return super(Account, self).delete(account_ids)
+    @classmethod
+    def delete(cls, accounts):
+        MoveLine = Pool().get('account.move.line')
+        childs = cls.search([
+                ('parent', 'child_of', [a.id for a in accounts]),
+                ])
+        if MoveLine.search([
+                    ('account', 'in', [a.id for a in childs]),
+                    ]):
+            cls.raise_user_error('delete_account_containing_move_lines')
+        super(Account, cls).delete(accounts)
 
-    def update_account(self, account, template2account=None,
-            template2type=None):
+    def update_account(self, template2account=None, template2type=None):
         '''
         Update recursively accounts based on template.
-
-        :param account: an account id or the BrowseRecord of the account
-        :param template2account: a dictionary with template id as key
-                and account id as value, used to convert template id
-                into account. The dictionary is filled with new accounts
-        :param template2type: a dictionary with type template id as key
-                and type id as value, used to convert type template id
-                into type.
+        template2account is a dictionary with template id as key and account id
+        as value, used to convert template id into account. The dictionary is
+        filled with new accounts.
+        template2type is a dictionary with type template id as key and type id
+        as value, used to convert type template id into type.
         '''
-        template_obj = Pool().get('account.account.template')
-        lang_obj = Pool().get('ir.lang')
+        Lang = Pool().get('ir.lang')
 
         if template2account is None:
             template2account = {}
@@ -951,53 +884,46 @@
         if template2type is None:
             template2type = {}
 
-        if isinstance(account, (int, long)):
-            account = self.browse(account)
+        if self.template:
+            vals = self.template._get_account_value(account=self)
+            if self.type.id != template2type.get(self.template.type.id):
+                vals['type'] = template2type.get(self.template.type.id)
+            if vals:
+                self.write([self], vals)
 
-        if account.template:
-            vals = template_obj._get_account_value(account.template,
-                    account=account)
-            if account.type.id != template2type.get(account.template.type.id):
-                vals['type'] = template2type.get(account.template.type.id)
-            if vals:
-                self.write(account.id, vals)
-
-            prev_lang = account._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in account.template._columns.iteritems():
+            for field_name, field in self.template._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = account.template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self.template, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                account.setLang(lang)
-                data = {}
-                for field_name, field in account.template._columns.iteritems():
-                    if (getattr(field, 'translate', False)
-                            and account.template[field_name] !=
-                            prev_data[field_name]):
-                        data[field_name] = account.template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        self.write(account.id, data)
-            account.setLang(prev_lang)
-            template2account[account.template.id] = account.id
+                with Transaction().set_context(language=lang):
+                    account = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in (
+                            account.template._fields.iteritems()):
+                        if (getattr(field, 'translate', False)
+                                and (getattr(account.template, field_name) !=
+                                    prev_data[field_name])):
+                            data[field_name] = getattr(account.template,
+                                field_name)
+                    if data:
+                        self.write([account], data)
+            template2account[self.template.id] = self.id
 
-        for child in account.childs:
-            self.update_account(child, template2account=template2account,
-                    template2type=template2type)
+        for child in self.childs:
+            child.update_account(template2account=template2account,
+                template2type=template2type)
 
-    def update_account_taxes(self, account, template2account, template2tax):
+    def update_account_taxes(self, template2account, template2tax):
         '''
         Update recursively account taxes base on template.
-
-        :param account: the account id or the BrowseRecord of account
-        :param template2account: a dictionary with template id as key
-            and account id as value, used to convert template id into
-            account.
-        :param template2tax: a dictionary with tax template id as key
-            and tax id as value, used to convert tax template id into
-            tax.
+        template2account is a dictionary with template id as key and account id
+        as value, used to convert template id into account.
+        template2tax is a dictionary with tax template id as key and tax id as
+        value, used to convert tax template id into tax.
         '''
         if template2account is None:
             template2account = {}
@@ -1005,28 +931,23 @@
         if template2tax is None:
             template2tax = {}
 
-        if isinstance(account, (int, long)):
-            account = self.browse(account)
-
-        if account.template:
-            if account.template.taxes:
-                tax_ids = [template2tax[x.id] for x in account.template.taxes
+        if self.template:
+            if self.template.taxes:
+                tax_ids = [template2tax[x.id] for x in self.template.taxes
                         if x.id in template2tax]
-                old_tax_ids = [x.id for x in account.taxes]
+                old_tax_ids = [x.id for x in self.taxes]
                 for tax_id in tax_ids:
                     if tax_id not in old_tax_ids:
-                        self.write(account.id, {
+                        self.write([self], {
                             'taxes': [
-                                ('add', template2tax[x.id]) \
-                                        for x in account.template.taxes \
-                                        if x.id in template2tax],
-                            })
+                                    ('add', template2tax[x.id])
+                                    for x in self.template.taxes
+                                    if x.id in template2tax],
+                                })
                         break
 
-        for child in account.childs:
-            self.update_account_taxes(child, template2account, template2tax)
-
-Account()
+        for child in self.childs:
+            child.update_account_taxes(template2account, template2tax)
 
 
 class AccountDeferral(ModelSQL, ModelView):
@@ -1035,9 +956,7 @@
 
     It is used to deferral the debit/credit of account by fiscal year.
     '''
-    _name = 'account.account.deferral'
-    _description = 'Account Deferral'
-
+    __name__ = 'account.account.deferral'
     account = fields.Many2One('account.account', 'Account', required=True,
             select=True)
     fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
@@ -1049,62 +968,51 @@
     currency_digits = fields.Function(fields.Integer('Currency Digits'),
             'get_currency_digits')
 
-    def __init__(self):
-        super(AccountDeferral, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(AccountDeferral, cls).__setup__()
+        cls._sql_constraints += [
             ('deferral_uniq', 'UNIQUE(account, fiscalyear)',
                 'Deferral must be unique by account and fiscal year'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'write_deferral': 'You can not modify Account Deferral records',
             })
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for deferral in self.browse(ids):
-            res[deferral.id] = deferral.account.currency_digits
-        return res
+    def get_currency_digits(self, name):
+        return self.account.currency_digits
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for deferral in self.browse(ids):
-            res[deferral.id] = deferral.account.rec_name + ' - ' + \
-                    deferral.fiscalyear.rec_name
-        return res
+    def get_rec_name(self, name):
+        return '%s - %s' % (self.account.rec_name, self.fiscalyear.rec_name)
 
-    def search_rec_name(self, name, clause):
-        ids = self.search(['OR',
-            ('account.rec_name',) + clause[1:],
-            ('fiscalyear.rec_name',) + clause[1:],
-            ])
-        return [('id', 'in', ids)]
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        deferrals = cls.search(['OR',
+                ('account.rec_name',) + clause[1:],
+                ('fiscalyear.rec_name',) + clause[1:],
+                ])
+        return [('id', 'in', [d.id for d in deferrals])]
 
-    def write(self, ids, vals):
-        self.raise_user_error('write_deferral')
-
-AccountDeferral()
+    @classmethod
+    def write(cls, deferrals, vals):
+        cls.raise_user_error('write_deferral')
 
 
 class OpenChartAccountStart(ModelView):
     'Open Chart of Accounts'
-    _name = 'account.open_chart.start'
-    _description = __doc__
+    __name__ = 'account.open_chart.start'
     fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
             help='Leave empty for all open fiscal year')
     posted = fields.Boolean('Posted Move', help='Show only posted move')
 
-    def default_posted(self):
+    @staticmethod
+    def default_posted():
         return False
 
-OpenChartAccountStart()
-
 
 class OpenChartAccount(Wizard):
     'Open Chart of Accounts'
-    _name = 'account.open_chart'
-
+    __name__ = 'account.open_chart'
     start = StateView('account.open_chart.start',
         'account.open_chart_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1112,23 +1020,21 @@
             ])
     open_ = StateAction('account.act_account_tree2')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         action['pyson_context'] = PYSONEncoder().encode({
-            'fiscalyear': session.start.fiscalyear.id,
-            'posted': session.start.posted,
+            'fiscalyear': (self.start.fiscalyear.id
+                    if self.start.fiscalyear else None),
+            'posted': self.start.posted,
             })
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
 
-OpenChartAccount()
-
 
 class PrintGeneralLegderStart(ModelView):
     'Print General Ledger'
-    _name = 'account.print_general_ledger.start'
-    _description = __doc__
+    __name__ = 'account.print_general_ledger.start'
     fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
             required=True, on_change=['fiscalyear'])
     start_period = fields.Many2One('account.period', 'Start Period',
@@ -1147,35 +1053,34 @@
     empty_account = fields.Boolean('Empty Account',
             help='With account without move')
 
-    def default_fiscalyear(self):
-        fiscalyear_obj = Pool().get('account.fiscalyear')
-        fiscalyear_id = fiscalyear_obj.find(
-                Transaction().context.get('company'), exception=False)
-        if fiscalyear_id:
-            return fiscalyear_id
+    @staticmethod
+    def default_fiscalyear():
+        FiscalYear = Pool().get('account.fiscalyear')
+        return FiscalYear.find(
+            Transaction().context.get('company'), exception=False)
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_posted(self):
+    @staticmethod
+    def default_posted():
         return False
 
-    def default_empty_account(self):
+    @staticmethod
+    def default_empty_account():
         return False
 
-    def on_change_fiscalyear(self, vals):
+    def on_change_fiscalyear(self):
         return {
             'start_period': None,
             'end_period': None,
-        }
-
-PrintGeneralLegderStart()
+            }
 
 
 class PrintGeneralLegder(Wizard):
     'Print General Legder'
-    _name = 'account.print_general_ledger'
-
+    __name__ = 'account.print_general_ledger'
     start = StateView('account.print_general_ledger.start',
         'account.print_general_ledger_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1183,143 +1088,147 @@
             ])
     print_ = StateAction('account.report_general_ledger')
 
-    def do_print_(self, session, action):
-        if session.start.start_period:
-            start_period = session.start.start_period.id
+    def do_print_(self, action):
+        if self.start.start_period:
+            start_period = self.start.start_period.id
         else:
             start_period = None
-        if session.start.end_period:
-            end_period = session.start.end_period.id
+        if self.start.end_period:
+            end_period = self.start.end_period.id
         else:
             end_period = None
         data = {
-            'company': session.start.company.id,
-            'fiscalyear': session.start.fiscalyear.id,
+            'company': self.start.company.id,
+            'fiscalyear': self.start.fiscalyear.id,
             'start_period': start_period,
             'end_period': end_period,
-            'posted': session.start.posted,
-            'empty_account': session.start.empty_account,
+            'posted': self.start.posted,
+            'empty_account': self.start.empty_account,
             }
         return action, data
 
-    def transition_print_(self, session):
+    def transition_print_(self):
         return 'end'
 
-PrintGeneralLegder()
-
 
 class GeneralLegder(Report):
-    _name = 'account.general_ledger'
+    __name__ = 'account.general_ledger'
 
-    def parse(self, report, objects, data, localcontext):
+    @classmethod
+    def parse(cls, report, objects, data, localcontext):
         pool = Pool()
-        account_obj = pool.get('account.account')
-        period_obj = pool.get('account.period')
-        company_obj = pool.get('company.company')
+        Account = pool.get('account.account')
+        Period = pool.get('account.period')
+        Company = pool.get('company.company')
 
-        company = company_obj.browse(data['company'])
+        company = Company(data['company'])
 
-        account_ids = account_obj.search([
+        accounts = Account.search([
                 ('company', '=', data['company']),
                 ('kind', '!=', 'view'),
                 ], order=[('code', 'ASC'), ('id', 'ASC')])
 
         start_period_ids = [0]
+        start_periods = []
         if data['start_period']:
-            start_period = period_obj.browse(data['start_period'])
-            start_period_ids = period_obj.search([
+            start_period = Period(data['start_period'])
+            start_periods = Period.search([
                     ('fiscalyear', '=', data['fiscalyear']),
                     ('end_date', '<=', start_period.start_date),
                     ])
+            start_period_ids = [p.id for p in start_periods]
 
         with Transaction().set_context(
                 fiscalyear=data['fiscalyear'],
                 periods=start_period_ids,
                 posted=data['posted']):
-            start_accounts = account_obj.browse(account_ids)
+            start_accounts = Account.browse(accounts)
         id2start_account = {}
         for account in start_accounts:
             id2start_account[account.id] = account
 
         end_period_ids = []
         if data['end_period']:
-            end_period = period_obj.browse(data['end_period'])
-            end_period_ids = period_obj.search([
+            end_period = Period(data['end_period'])
+            end_periods = Period.search([
                     ('fiscalyear', '=', data['fiscalyear']),
                     ('end_date', '<=', end_period.start_date),
                     ])
-            if data['end_period'] not in end_period_ids:
-                end_period_ids.append(data['end_period'])
+            if end_period not in end_periods:
+                end_periods.append(end_period)
         else:
-            end_period_ids = period_obj.search([
+            end_periods = Period.search([
                     ('fiscalyear', '=', data['fiscalyear']),
                     ])
+        end_period_ids = [p.id for p in end_periods]
 
         with Transaction().set_context(
                 fiscalyear=data['fiscalyear'],
                 periods=end_period_ids,
                 posted=data['posted']):
-            end_accounts = account_obj.browse(account_ids)
+            end_accounts = Account.browse(accounts)
         id2end_account = {}
         for account in end_accounts:
             id2end_account[account.id] = account
 
-        periods = period_obj.browse(end_period_ids)
+        periods = end_periods
         periods.sort(lambda x, y: cmp(x.start_date, y.start_date))
         localcontext['start_period'] = periods[0]
         periods.sort(lambda x, y: cmp(x.end_date, y.end_date))
         localcontext['end_period'] = periods[-1]
 
         if not data['empty_account']:
-            account_id2lines = self.get_lines(account_ids,
-                end_period_ids, data['posted'])
-            for account_id in (set(account_ids) - set(account_id2lines)):
-                account_ids.remove(account_id)
+            account2lines = cls.get_lines(accounts,
+                end_periods, data['posted'])
+            for account in (set(accounts) - set(account2lines)):
+                accounts.remove(account)
             account_id2lines = None
 
-        account_id2lines = self.lines(account_ids,
-            list(set(end_period_ids).difference(set(start_period_ids))),
+        account_id2lines = cls.lines(accounts,
+            list(set(end_periods).difference(set(start_periods))),
             data['posted'])
 
-        localcontext['accounts'] = account_obj.browse(account_ids)
+        localcontext['accounts'] = accounts
         localcontext['id2start_account'] = id2start_account
         localcontext['id2end_account'] = id2end_account
         localcontext['digits'] = company.currency.digits
         localcontext['lines'] = lambda account_id: account_id2lines[account_id]
         localcontext['company'] = company
 
-        return super(GeneralLegder, self).parse(report, objects, data,
+        return super(GeneralLegder, cls).parse(report, objects, data,
             localcontext)
 
-    def get_lines(self, account_ids, period_ids, posted):
-        move_line_obj = Pool().get('account.move.line')
+    @classmethod
+    def get_lines(cls, accounts, periods, posted):
+        MoveLine = Pool().get('account.move.line')
         clause = [
-            ('account', 'in', account_ids),
-            ('period', 'in', period_ids),
+            ('account', 'in', [a.id for a in accounts]),
+            ('period', 'in', [p.id for p in periods]),
             ('state', '!=', 'draft'),
             ]
         if posted:
             clause.append(('move.state', '=', 'posted'))
-        move_ids = move_line_obj.search(clause, order=[])
+        moves = MoveLine.search(clause, order=[])
         res = {}
-        for move in move_line_obj.browse(move_ids):
-            res.setdefault(move.account.id, []).append(move)
+        for move in moves:
+            res.setdefault(move.account, []).append(move)
         return res
 
-    def lines(self, account_ids, period_ids, posted):
-        move_obj = Pool().get('account.move')
-        res = dict((account_id, []) for account_id in account_ids)
-        account_id2lines = self.get_lines(account_ids, period_ids, posted)
+    @classmethod
+    def lines(cls, accounts, periods, posted):
+        Move = Pool().get('account.move')
+        res = dict((a.id, []) for a in accounts)
+        account2lines = cls.get_lines(accounts, periods, posted)
 
-        state_selections = dict(move_obj.fields_get(
-            fields_names=['state'])['state']['selection'])
+        state_selections = dict(Move.fields_get(
+                fields_names=['state'])['state']['selection'])
 
-        for account_id, lines in account_id2lines.iteritems():
+        for account, lines in account2lines.iteritems():
             lines.sort(lambda x, y: cmp(x.date, y.date))
             balance = Decimal('0.0')
             for line in lines:
                 balance += line.debit - line.credit
-                res[account_id].append({
+                res[account.id].append({
                         'date': line.date,
                         'debit': line.debit,
                         'credit': line.credit,
@@ -1330,13 +1239,10 @@
                         })
         return res
 
-GeneralLegder()
-
 
 class PrintTrialBalanceStart(ModelView):
     'Print Trial Balance'
-    _name = 'account.print_trial_balance.start'
-    _description = __doc__
+    __name__ = 'account.print_trial_balance.start'
     fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
             required=True, on_change=['fiscalyear'],
             depends=['start_period', 'end_period'])
@@ -1357,35 +1263,34 @@
     empty_account = fields.Boolean('Empty Account',
             help='With account without move')
 
-    def default_fiscalyear(self):
-        fiscalyear_obj = Pool().get('account.fiscalyear')
-        fiscalyear_id = fiscalyear_obj.find(
-                Transaction().context.get('company'), exception=False)
-        if fiscalyear_id:
-            return fiscalyear_id
+    @staticmethod
+    def default_fiscalyear():
+        FiscalYear = Pool().get('account.fiscalyear')
+        return FiscalYear.find(
+            Transaction().context.get('company'), exception=False)
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_posted(self):
+    @staticmethod
+    def default_posted():
         return False
 
-    def default_empty_account(self):
+    @staticmethod
+    def default_empty_account():
         return False
 
-    def on_change_fiscalyear(self, vals):
+    def on_change_fiscalyear(self):
         return {
             'start_period': None,
             'end_period': None,
-        }
-
-PrintTrialBalanceStart()
+            }
 
 
 class PrintTrialBalance(Wizard):
     'Print Trial Balance'
-    _name = 'account.print_trial_balance'
-
+    __name__ = 'account.print_trial_balance'
     start = StateView('account.print_trial_balance.start',
         'account.print_general_ledger_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1393,90 +1298,91 @@
             ])
     print_ = StateAction('account.report_trial_balance')
 
-    def do_print_(self, session, action):
-        if session.start.start_period:
-            start_period = session.start.start_period.id
+    def do_print_(self, action):
+        if self.start.start_period:
+            start_period = self.start.start_period.id
         else:
             start_period = None
-        if session.start.end_period:
-            end_period = session.start.end_period.id
+        if self.start.end_period:
+            end_period = self.start.end_period.id
         else:
             end_period = None
         data = {
-            'company': session.start.company.id,
-            'fiscalyear': session.start.fiscalyear.id,
+            'company': self.start.company.id,
+            'fiscalyear': self.start.fiscalyear.id,
             'start_period': start_period,
             'end_period': end_period,
-            'posted': session.start.posted,
-            'empty_account': session.start.empty_account,
+            'posted': self.start.posted,
+            'empty_account': self.start.empty_account,
             }
         return action, data
 
-    def transition_print_(self, session):
+    def transition_print_(self):
         return 'end'
 
-PrintTrialBalance()
-
 
 class TrialBalance(Report):
-    _name = 'account.trial_balance'
+    __name__ = 'account.trial_balance'
 
-    def parse(self, report, objects, data, localcontext):
+    @classmethod
+    def parse(cls, report, objects, data, localcontext):
         pool = Pool()
-        account_obj = pool.get('account.account')
-        period_obj = pool.get('account.period')
-        company_obj = pool.get('company.company')
+        Account = pool.get('account.account')
+        Period = pool.get('account.period')
+        Company = pool.get('company.company')
 
-        company = company_obj.browse(data['company'])
+        company = Company(data['company'])
 
-        account_ids = account_obj.search([
-            ('company', '=', data['company']),
-            ('kind', '!=', 'view'),
-            ])
+        accounts = Account.search([
+                ('company', '=', data['company']),
+                ('kind', '!=', 'view'),
+                ])
 
-        start_period_ids = [0]
+        start_periods = []
         if data['start_period']:
-            start_period = period_obj.browse(data['start_period'])
-            start_period_ids = period_obj.search([
+            start_period = Period(data['start_period'])
+            start_periods = Period.search([
                     ('fiscalyear', '=', data['fiscalyear']),
                     ('end_date', '<=', start_period.start_date),
                     ])
 
-        end_period_ids = []
         if data['end_period']:
-            end_period = period_obj.browse(data['end_period'])
-            end_period_ids = period_obj.search([
+            end_period = Period(data['end_period'])
+            end_periods = Period.search([
                     ('fiscalyear', '=', data['fiscalyear']),
                     ('end_date', '<=', end_period.start_date),
                     ])
-            end_period_ids = list(set(end_period_ids).difference(
-                    set(start_period_ids)))
-            if data['end_period'] not in end_period_ids:
-                end_period_ids.append(data['end_period'])
+            end_periods = list(set(end_periods).difference(
+                    set(start_periods)))
+            if end_period not in end_periods:
+                end_periods.append(end_period)
         else:
-            end_period_ids = period_obj.search([
+            end_periods = Period.search([
                     ('fiscalyear', '=', data['fiscalyear']),
                     ])
-            end_period_ids = list(set(end_period_ids).difference(
-                    set(start_period_ids)))
+            end_periods = list(set(end_periods).difference(
+                    set(start_periods)))
+
+        start_period_ids = [p.id for p in start_periods] or [0]
+        end_period_ids = [p.id for p in end_periods]
 
         with Transaction().set_context(
                 fiscalyear=data['fiscalyear'],
                 periods=start_period_ids,
                 posted=data['posted']):
-            start_accounts = account_obj.browse(account_ids)
+            start_accounts = Account.browse(accounts)
 
         with Transaction().set_context(
                 fiscalyear=None,
                 periods=end_period_ids,
                 posted=data['posted']):
-            in_accounts = account_obj.browse(account_ids)
+            in_accounts = Account.browse(accounts)
 
         with Transaction().set_context(
                 fiscalyear=data['fiscalyear'],
                 periods=start_period_ids + end_period_ids,
                 posted=data['posted']):
-            end_accounts = account_obj.browse(account_ids)
+            end_accounts = Account.browse(accounts)
 
         to_remove = []
         if not data['empty_account']:
@@ -1499,7 +1405,7 @@
                     'end_balance': end_account.balance,
                     })
 
-        periods = period_obj.browse(end_period_ids)
+        periods = end_periods
 
         localcontext['accounts'] = accounts
         periods.sort(key=operator.attrgetter('start_date'))
@@ -1508,45 +1414,43 @@
         localcontext['end_period'] = periods[-1]
         localcontext['company'] = company
         localcontext['digits'] = company.currency.digits
-        localcontext['sum'] = lambda accounts, field: self.sum(accounts, field)
+        localcontext['sum'] = lambda accounts, field: cls.sum(accounts, field)
 
-        return super(TrialBalance, self).parse(report, objects, data,
+        return super(TrialBalance, cls).parse(report, objects, data,
             localcontext)
 
-    def sum(self, accounts, field):
+    @classmethod
+    def sum(cls, accounts, field):
         amount = Decimal('0.0')
         for account in accounts:
             amount += account[field]
         return amount
 
-TrialBalance()
-
 
 class OpenBalanceSheetStart(ModelView):
     'Open Balance Sheet'
-    _name = 'account.open_balance_sheet.start'
-    _description = __doc__
+    __name__ = 'account.open_balance_sheet.start'
     date = fields.Date('Date', required=True)
     company = fields.Many2One('company.company', 'Company', required=True)
     posted = fields.Boolean('Posted Move', help='Show only posted move')
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_date():
+        Date_ = Pool().get('ir.date')
+        return Date_.today()
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_posted(self):
+    @staticmethod
+    def default_posted():
         return False
 
-OpenBalanceSheetStart()
-
 
 class OpenBalanceSheet(Wizard):
     'Open Balance Sheet'
-    _name = 'account.open_balance_sheet'
-
+    __name__ = 'account.open_balance_sheet'
     start = StateView('account.open_balance_sheet.start',
         'account.open_balance_sheet_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1554,38 +1458,34 @@
             ])
     open_ = StateAction('account.act_account_balance_sheet_tree')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
+        Lang = pool.get('ir.lang')
 
-        company = session.start.company
-        lang_id, = lang_obj.search([
+        company = self.start.company
+        lang, = Lang.search([
                 ('code', '=', Transaction().language),
                 ])
-        lang = lang_obj.browse(lang_id)
 
-        date = lang_obj.strftime(session.start.date, lang.code, lang.date)
+        date = Lang.strftime(self.start.date, lang.code, lang.date)
 
         action['pyson_context'] = PYSONEncoder().encode({
-                'date': Date(session.start.date.year,
-                    session.start.date.month,
-                    session.start.date.day),
-                'posted': session.start.posted,
+                'date': Date(self.start.date.year,
+                    self.start.date.month,
+                    self.start.date.day),
+                'posted': self.start.posted,
                 'company': company.id,
                 })
-        action['name'] += ' - ' + date + ' - ' + company.name
+        action['name'] += '%s - %s' % (date, company.name)
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
 
-OpenBalanceSheet()
-
 
 class OpenIncomeStatementStart(ModelView):
     'Open Income Statement'
-    _name = 'account.open_income_statement.start'
-    _description = __doc__
+    __name__ = 'account.open_income_statement.start'
     fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
             required=True, on_change=['fiscalyear'],
             depends=['start_period', 'end_period'])
@@ -1604,32 +1504,30 @@
     company = fields.Many2One('company.company', 'Company', required=True)
     posted = fields.Boolean('Posted Move', help='Show only posted move')
 
-    def default_fiscalyear(self):
-        fiscalyear_obj = Pool().get('account.fiscalyear')
-        fiscalyear_id = fiscalyear_obj.find(
-                Transaction().context.get('company'), exception=False)
-        if fiscalyear_id:
-            return fiscalyear_id
+    @staticmethod
+    def default_fiscalyear():
+        FiscalYear = Pool().get('account.fiscalyear')
+        return FiscalYear.find(
+            Transaction().context.get('company'), exception=False)
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_posted(self):
+    @staticmethod
+    def default_posted():
         return False
 
-    def on_change_fiscalyear(self, vals):
+    def on_change_fiscalyear(self):
         return {
             'start_period': None,
             'end_period': None,
         }
 
-OpenIncomeStatementStart()
-
 
 class OpenIncomeStatement(Wizard):
     'Open Income Statement'
-    _name = 'account.open_income_statement'
-
+    __name__ = 'account.open_income_statement'
     start = StateView('account.open_income_statement.start',
         'account.open_income_statement_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1637,70 +1535,62 @@
             ])
     open_ = StateAction('account.act_account_income_statement_tree')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         pool = Pool()
-        period_obj = pool.get('account.period')
+        Period = pool.get('account.period')
 
-        start_period_ids = [0]
-        if session.start.start_period:
-            start_period_ids = period_obj.search([
-                    ('fiscalyear', '=', session.start.fiscalyear.id),
-                    ('end_date', '<=', session.start.start_period.start_date),
+        start_periods = []
+        if self.start.start_period:
+            start_periods = Period.search([
+                    ('fiscalyear', '=', self.start.fiscalyear.id),
+                    ('end_date', '<=', self.start.start_period.start_date),
                     ])
 
-        end_period_ids = []
-        if session.start.end_period:
-            end_period_ids = period_obj.search([
-                    ('fiscalyear', '=', session.start.fiscalyear.id),
-                    ('end_date', '<=', session.start.end_period.start_date),
+        end_periods = []
+        if self.start.end_period:
+            end_periods = Period.search([
+                    ('fiscalyear', '=', self.start.fiscalyear.id),
+                    ('end_date', '<=', self.start.end_period.start_date),
                     ])
-            end_period_ids = list(set(end_period_ids).difference(
-                    set(start_period_ids)))
-            if session.start.end_period.id not in end_period_ids:
-                end_period_ids.append(session.start.end_period.id)
+            end_periods = list(set(end_periods).difference(
+                    set(start_periods)))
+            if self.start.end_period not in end_periods:
+                end_periods.append(self.start.end_period)
         else:
-            end_period_ids = period_obj.search([
-                    ('fiscalyear', '=', session.start.fiscalyear.id),
+            end_periods = Period.search([
+                    ('fiscalyear', '=', self.start.fiscalyear.id),
                     ])
-            end_period_ids = list(set(end_period_ids).difference(
-                    set(start_period_ids)))
+            end_periods = list(set(end_periods).difference(
+                    set(start_periods)))
 
         action['pyson_context'] = PYSONEncoder().encode({
-                'periods': end_period_ids,
-                'posted': session.start.posted,
-                'company': session.start.company.id,
+                'periods': [p.id for p in end_periods],
+                'posted': self.start.posted,
+                'company': self.start.company.id,
                 })
         return action, {}
 
-OpenIncomeStatement()
-
 
 class CreateChartStart(ModelView):
     'Create Chart'
-    _name = 'account.create_chart.start'
-    _description = __doc__
-
-CreateChartStart()
+    __name__ = 'account.create_chart.start'
 
 
 class CreateChartAccount(ModelView):
     'Create Chart'
-    _name = 'account.create_chart.account'
-    _description = __doc__
+    __name__ = 'account.create_chart.account'
     company = fields.Many2One('company.company', 'Company', required=True)
     account_template = fields.Many2One('account.account.template',
             'Account Template', required=True, domain=[('parent', '=', None)])
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-CreateChartAccount()
 
-
-class CreateChartPropertites(ModelView):
+class CreateChartProperties(ModelView):
     'Create Chart'
-    _name = 'account.create_chart.properties'
-    _description = __doc__
+    __name__ = 'account.create_chart.properties'
     company = fields.Many2One('company.company', 'Company')
     account_receivable = fields.Many2One('account.account',
             'Default Receivable Account',
@@ -1717,13 +1607,10 @@
             ],
             depends=['company'])
 
-CreateChartPropertites()
-
 
 class CreateChart(Wizard):
     'Create Chart'
-    _name = 'account.create_chart'
-
+    __name__ = 'account.create_chart'
     start = StateView('account.create_chart.start',
         'account.create_chart_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1742,158 +1629,138 @@
             ])
     create_properties = StateTransition()
 
-    def transition_create_account(self, session):
+    def transition_create_account(self):
         pool = Pool()
-        account_type_template_obj = \
-                pool.get('account.account.type.template')
-        account_template_obj = pool.get('account.account.template')
-        tax_code_template_obj = pool.get('account.tax.code.template')
-        tax_template_obj = pool.get('account.tax.template')
-        tax_rule_template_obj = pool.get('account.tax.rule.template')
-        tax_rule_line_template_obj = \
+        TaxCodeTemplate = pool.get('account.tax.code.template')
+        TaxTemplate = pool.get('account.tax.template')
+        TaxRuleTemplate = pool.get('account.tax.rule.template')
+        TaxRuleLineTemplate = \
                 pool.get('account.tax.rule.line.template')
 
         with Transaction().set_context(language=CONFIG['language']):
-            account_template = session.account.account_template
+            account_template = self.account.account_template
 
             # Create account types
             template2type = {}
-            account_type_template_obj.create_type(account_template.type,
-                session.account.company.id, template2type=template2type)
+            account_template.type.create_type(self.account.company.id,
+                template2type=template2type)
 
             # Create accounts
             template2account = {}
-            account_template_obj.create_account(account_template,
-                session.account.company.id,
-                template2account=template2account,
-                template2type=template2type)
+            account_template.create_account(self.account.company.id,
+                template2account=template2account, template2type=template2type)
 
             # Create tax codes
             template2tax_code = {}
-            tax_code_template_ids = tax_code_template_obj.search([
+            tax_code_templates = TaxCodeTemplate.search([
                     ('account', '=', account_template.id),
                     ('parent', '=', None),
                     ])
-            for tax_code_template in tax_code_template_obj.browse(
-                    tax_code_template_ids):
-                tax_code_template_obj.create_tax_code(tax_code_template,
-                    session.account.company.id,
+            for tax_code_template in tax_code_templates:
+                tax_code_template.create_tax_code(self.account.company.id,
                     template2tax_code=template2tax_code)
 
             # Create taxes
             template2tax = {}
-            tax_template_ids = tax_template_obj.search([
+            tax_templates = TaxTemplate.search([
                     ('account', '=', account_template.id),
                     ('parent', '=', None),
                     ])
-            for tax_template in tax_template_obj.browse(tax_template_ids):
-                tax_template_obj.create_tax(tax_template,
-                    session.account.company.id,
+            for tax_template in tax_templates:
+                tax_template.create_tax(self.account.company.id,
                     template2tax_code=template2tax_code,
                     template2account=template2account,
                     template2tax=template2tax)
 
             # Update taxes on accounts
-            account_template_obj.update_account_taxes(account_template,
-                    template2account, template2tax)
+            account_template.update_account_taxes(template2account,
+                template2tax)
 
             # Create tax rules
             template2rule = {}
-            tax_rule_template_ids = tax_rule_template_obj.search([
+            tax_rule_templates = TaxRuleTemplate.search([
                     ('account', '=', account_template.id),
                     ])
-            for tax_rule_template in tax_rule_template_obj.browse(
-                    tax_rule_template_ids):
-                tax_rule_template_obj.create_rule(tax_rule_template,
-                    session.account.company.id, template2rule=template2rule)
+            for tax_rule_template in tax_rule_templates:
+                tax_rule_template.create_rule(self.account.company.id,
+                    template2rule=template2rule)
 
             # Create tax rule lines
             template2rule_line = {}
-            tax_rule_line_template_ids = tax_rule_line_template_obj.search([
+            tax_rule_line_templates = TaxRuleLineTemplate.search([
                     ('rule.account', '=', account_template.id),
                     ])
-            for tax_rule_line_template in tax_rule_line_template_obj.browse(
-                    tax_rule_line_template_ids):
-                tax_rule_line_template_obj.create_rule_line(
-                    tax_rule_line_template, template2tax, template2rule,
-                    template2rule_line=template2rule_line)
+            for tax_rule_line_template in tax_rule_line_templates:
+                tax_rule_line_template.create_rule_line(template2tax,
+                    template2rule, template2rule_line=template2rule_line)
         return 'properties'
 
-    def default_properties(self, session, fields):
+    def default_properties(self, fields):
         return {
-            'company': session.account.company.id,
+            'company': self.account.company.id,
             }
 
-    def transition_create_properties(self, session):
+    def transition_create_properties(self):
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        model_field_obj = pool.get('ir.model.field')
+        Property = pool.get('ir.property')
+        ModelField = pool.get('ir.model.field')
 
-        with Transaction().set_context(company=session.properties.company.id):
-            account_receivable_field_id, = model_field_obj.search([
+        with Transaction().set_context(company=self.properties.company.id):
+            account_receivable_field, = ModelField.search([
                     ('model.model', '=', 'party.party'),
                     ('name', '=', 'account_receivable'),
                     ], limit=1)
-            property_ids = property_obj.search([
-                    ('field', '=', account_receivable_field_id),
+            properties = Property.search([
+                    ('field', '=', account_receivable_field.id),
                     ('res', '=', None),
-                    ('company', '=', session.properties.company.id),
+                    ('company', '=', self.properties.company.id),
                     ])
             with Transaction().set_user(0):
-                property_obj.delete(property_ids)
-                if session.properties.account_receivable:
-                    property_obj.create({
-                            'field': account_receivable_field_id,
+                Property.delete(properties)
+                if self.properties.account_receivable:
+                    Property.create({
+                            'field': account_receivable_field.id,
                             'value': 'account.account,' + \
-                                str(session.properties.account_receivable.id),
-                            'company': session.properties.company.id,
+                                str(self.properties.account_receivable.id),
+                            'company': self.properties.company.id,
                             })
 
-            account_payable_field_id, = model_field_obj.search([
+            account_payable_field, = ModelField.search([
                     ('model.model', '=', 'party.party'),
                     ('name', '=', 'account_payable'),
                     ], limit=1)
-            property_ids = property_obj.search([
-                    ('field', '=', account_payable_field_id),
+            properties = Property.search([
+                    ('field', '=', account_payable_field.id),
                     ('res', '=', None),
-                    ('company', '=', session.properties.company.id),
+                    ('company', '=', self.properties.company.id),
                     ])
             with Transaction().set_user(0):
-                property_obj.delete(property_ids)
-                if session.properties.account_payable:
-                    property_obj.create({
-                            'field': account_payable_field_id,
+                Property.delete(properties)
+                if self.properties.account_payable:
+                    Property.create({
+                            'field': account_payable_field.id,
                             'value': 'account.account,' + \
-                                str(session.properties.account_payable.id),
-                            'company': session.properties.company.id,
+                                str(self.properties.account_payable.id),
+                            'company': self.properties.company.id,
                             })
         return 'end'
 
-CreateChart()
-
 
 class UpdateChartStart(ModelView):
     'Update Chart'
-    _name = 'account.update_chart.start'
-    _description = __doc__
+    __name__ = 'account.update_chart.start'
     account = fields.Many2One('account.account', 'Root Account',
             required=True, domain=[('parent', '=', None)])
 
-UpdateChartStart()
-
 
 class UpdateChartSucceed(ModelView):
     'Update Chart'
-    _name = 'account.update_chart.succeed'
-    _description = __doc__
-
-UpdateChartSucceed()
+    __name__ = 'account.update_chart.succeed'
 
 
 class UpdateChart(Wizard):
     'Update Chart'
-    _name = 'account.update_chart'
-
+    __name__ = 'account.update_chart'
     start = StateView('account.update_chart.start',
         'account.update_chart_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1905,159 +1772,141 @@
             Button('Ok', 'end', 'tryton-ok', default=True),
             ])
 
-    def transition_update(self, session):
+    def transition_update(self):
         pool = Pool()
-        account_type_obj = pool.get('account.account.type')
-        account_type_template_obj = \
-                pool.get('account.account.type.template')
-        account_obj = pool.get('account.account')
-        account_template_obj = pool.get('account.account.template')
-        tax_code_obj = pool.get('account.tax.code')
-        tax_code_template_obj = pool.get('account.tax.code.template')
-        tax_obj = pool.get('account.tax')
-        tax_template_obj = pool.get('account.tax.template')
-        tax_rule_obj = pool.get('account.tax.rule')
-        tax_rule_template_obj = pool.get('account.tax.rule.template')
-        tax_rule_line_obj = pool.get('account.tax.rule.line')
-        tax_rule_line_template_obj = \
+        TaxCode = pool.get('account.tax.code')
+        TaxCodeTemplate = pool.get('account.tax.code.template')
+        Tax = pool.get('account.tax')
+        TaxTemplate = pool.get('account.tax.template')
+        TaxRule = pool.get('account.tax.rule')
+        TaxRuleTemplate = pool.get('account.tax.rule.template')
+        TaxRuleLine = pool.get('account.tax.rule.line')
+        TaxRuleLineTemplate = \
                 pool.get('account.tax.rule.line.template')
 
-        account = session.start.account
+        account = self.start.account
 
         # Update account types
         template2type = {}
-        account_type_obj.update_type(account.type, template2type=template2type)
+        account.type.update_type(template2type=template2type)
         # Create missing account types
         if account.type.template:
-            account_type_template_obj.create_type(account.type.template,
-                    account.company.id, template2type=template2type)
+            account.type.template.create_type(account.company.id,
+                template2type=template2type)
 
         # Update accounts
         template2account = {}
-        account_obj.update_account(account, template2account=template2account,
-                template2type=template2type)
+        account.update_account(template2account=template2account,
+            template2type=template2type)
         # Create missing accounts
         if account.template:
-            account_template_obj.create_account(account.template,
-                    account.company.id, template2account=template2account,
-                    template2type=template2type)
+            account.template.create_account(account.company.id,
+                template2account=template2account, template2type=template2type)
 
         # Update tax codes
         template2tax_code = {}
-        tax_code_ids = tax_code_obj.search([
+        tax_codes = TaxCode.search([
             ('company', '=', account.company.id),
             ('parent', '=', None),
             ])
-        for tax_code in tax_code_obj.browse(tax_code_ids):
-            tax_code_obj.update_tax_code(tax_code,
-                    template2tax_code=template2tax_code)
+        for tax_code in tax_codes:
+            tax_code.update_tax_code(template2tax_code=template2tax_code)
         # Create missing tax codes
         if account.template:
-            tax_code_template_ids = tax_code_template_obj.search([
+            tax_code_templates = TaxCodeTemplate.search([
                 ('account', '=', account.template.id),
                 ('parent', '=', None),
                 ])
-            for tax_code_template in tax_code_template_obj.browse(
-                    tax_code_template_ids):
-                tax_code_template_obj.create_tax_code(tax_code_template,
-                        account.company.id,
-                        template2tax_code=template2tax_code)
+            for tax_code_template in tax_code_templates:
+                tax_code_template.create_tax_code(account.company.id,
+                    template2tax_code=template2tax_code)
 
         # Update taxes
         template2tax = {}
-        tax_ids = tax_obj.search([
+        taxes = Tax.search([
             ('company', '=', account.company.id),
             ('parent', '=', None),
             ])
-        for tax in tax_obj.browse(tax_ids):
-            tax_obj.update_tax(tax, template2tax_code=template2tax_code,
+        for tax in taxes:
+            tax.update_tax(template2tax_code=template2tax_code,
                     template2account=template2account,
                     template2tax=template2tax)
         # Create missing taxes
         if account.template:
-            tax_template_ids = tax_template_obj.search([
+            tax_templates = TaxTemplate.search([
                 ('account', '=', account.template.id),
                 ('parent', '=', None),
                 ])
-            for tax_template in tax_template_obj.browse(tax_template_ids):
-                tax_template_obj.create_tax(tax_template, account.company.id,
+            for tax_template in tax_templates:
+                tax_template.create_tax(account.company.id,
                         template2tax_code=template2tax_code,
                         template2account=template2account,
                         template2tax=template2tax)
 
         # Update taxes on accounts
-        account_obj.update_account_taxes(account, template2account,
-                template2tax)
+        account.update_account_taxes(template2account, template2tax)
 
         # Update tax rules
         template2rule = {}
-        tax_rule_ids = tax_rule_obj.search([
+        tax_rules = TaxRule.search([
             ('company', '=', account.company.id),
             ])
-        for tax_rule in tax_rule_obj.browse(tax_rule_ids):
-            tax_rule_obj.update_rule(tax_rule, template2rule=template2rule)
+        for tax_rule in tax_rules:
+            tax_rule.update_rule(template2rule=template2rule)
         # Create missing tax rules
         if account.template:
-            tax_rule_template_ids = tax_rule_template_obj.search([
+            tax_rule_templates = TaxRuleTemplate.search([
                 ('account', '=', account.template.id),
                 ])
-            for tax_rule_template in tax_rule_template_obj.browse(
-                    tax_rule_template_ids):
-                tax_rule_template_obj.create_rule(tax_rule_template,
-                        account.company.id, template2rule=template2rule)
+            for tax_rule_template in tax_rule_templates:
+                tax_rule_template.create_rule(account.company.id,
+                    template2rule=template2rule)
 
         # Update tax rule lines
         template2rule_line = {}
-        tax_rule_line_ids = tax_rule_line_obj.search([
+        tax_rule_lines = TaxRuleLine.search([
             ('rule.company', '=', account.company.id),
             ])
-        for tax_rule_line in tax_rule_line_obj.browse(tax_rule_line_ids):
-            tax_rule_line_obj.update_rule_line(tax_rule_line, template2tax,
-                    template2rule, template2rule_line=template2rule_line)
+        for tax_rule_line in tax_rule_lines:
+            tax_rule_line.update_rule_line(template2tax, template2rule,
+                template2rule_line=template2rule_line)
         # Create missing tax rule lines
         if account.template:
-            tax_rule_line_template_ids = tax_rule_line_template_obj.search([
+            tax_rule_line_templates = TaxRuleLineTemplate.search([
                 ('rule.account', '=', account.template.id),
                 ])
-            for tax_rule_line_template in tax_rule_line_template_obj.browse(
-                    tax_rule_line_template_ids):
-                tax_rule_line_template_obj.create_rule_line(
-                        tax_rule_line_template, template2tax, template2rule,
-                        template2rule_line=template2rule_line)
+            for tax_rule_line_template in tax_rule_line_templates:
+                tax_rule_line_template.create_rule_line(template2tax,
+                    template2rule, template2rule_line=template2rule_line)
         return 'succeed'
 
-UpdateChart()
-
 
 class OpenThirdPartyBalanceStart(ModelView):
     'Open Third Party Balance'
-    _name = 'account.open_third_party_balance.start'
-    _description = __doc__
+    __name__ = 'account.open_third_party_balance.start'
     company = fields.Many2One('company.company', 'Company', required=True)
     fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
             required=True)
     posted = fields.Boolean('Posted Move', help='Show only posted move')
 
-    def default_fiscalyear(self):
-        fiscalyear_obj = Pool().get('account.fiscalyear')
-        fiscalyear_id = fiscalyear_obj.find(
-                Transaction().context.get('company'), exception=False)
-        if fiscalyear_id:
-            return fiscalyear_id
+    @staticmethod
+    def default_fiscalyear():
+        Fiscalyear = Pool().get('account.fiscalyear')
+        return Fiscalyear.find(
+            Transaction().context.get('company'), exception=False)
 
-    def default_posted(self):
+    @staticmethod
+    def default_posted():
         return False
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-OpenThirdPartyBalanceStart()
-
 
 class OpenThirdPartyBalance(Wizard):
     'Open Third Party Balance'
-    _name = 'account.open_third_party_balance'
-
+    __name__ = 'account.open_third_party_balance'
     start = StateView('account.open_third_party_balance.start',
         'account.open_balance_sheet_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -2065,37 +1914,36 @@
             ])
     print_ = StateAction('account.report_third_party_balance')
 
-    def do_print_(self, session, action):
+    def do_print_(self, action):
         data = {
-            'company': session.start.company.id,
-            'fiscalyear': session.start.fiscalyear.id,
-            'posted': session.start.posted,
+            'company': self.start.company.id,
+            'fiscalyear': self.start.fiscalyear.id,
+            'posted': self.start.posted,
             }
         return action, data
 
-    def transition_print_(self, session):
+    def transition_print_(self):
         return 'end'
 
-OpenThirdPartyBalance()
-
 
 class ThirdPartyBalance(Report):
-    _name = 'account.third_party_balance'
+    __name__ = 'account.third_party_balance'
 
-    def parse(self, report, objects, data, localcontext):
+    @classmethod
+    def parse(cls, report, objects, data, localcontext):
         pool = Pool()
-        party_obj = pool.get('party.party')
-        move_line_obj = pool.get('account.move.line')
-        company_obj = pool.get('company.company')
-        date_obj = pool.get('ir.date')
+        Party = pool.get('party.party')
+        MoveLine = pool.get('account.move.line')
+        Company = pool.get('company.company')
+        Date = pool.get('ir.date')
         cursor = Transaction().cursor
 
-        company = company_obj.browse(data['company'])
+        company = Company(data['company'])
         localcontext['company'] = company
         localcontext['digits'] = company.currency.digits
         localcontext['fiscalyear'] = data['fiscalyear']
         with Transaction().set_context(context=localcontext):
-            line_query, _ = move_line_obj.query_get()
+            line_query, _ = MoveLine.query_get()
         if data['posted']:
             posted_clause = "AND m.state = 'posted' "
         else:
@@ -2116,11 +1964,11 @@
                     + posted_clause + \
                 'GROUP BY l.party ' \
                 'HAVING (SUM(l.debit) != 0 OR SUM(l.credit) != 0)',
-                (data['company'], date_obj.today()))
+                (data['company'], Date.today()))
 
         res = cursor.fetchall()
         id2party = {}
-        for party in party_obj.browse([x[0] for x in res]):
+        for party in Party.browse([x[0] for x in res]):
             id2party[party.id] = party
         objects = [{
             'name': id2party[x[0]].rec_name,
@@ -2133,16 +1981,13 @@
         localcontext['total_credit'] = sum((x['credit'] for x in objects))
         localcontext['total_solde'] = sum((x['solde'] for x in objects))
 
-        return super(ThirdPartyBalance, self).parse(report, objects, data,
+        return super(ThirdPartyBalance, cls).parse(report, objects, data,
             localcontext)
 
-ThirdPartyBalance()
-
 
 class OpenAgedBalanceStart(ModelView):
     'Open Aged Balance'
-    _name = 'account.open_aged_balance.start'
-    _description = __doc__
+    __name__ = 'account.open_aged_balance.start'
     company = fields.Many2One('company.company', 'Company', required=True)
     fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
             required=True)
@@ -2156,41 +2001,44 @@
         [('day', 'Day'), ('month', 'Month')], "Unit", required=True)
     posted = fields.Boolean('Posted Move', help='Show only posted move')
 
-    def default_fiscalyear(self):
-        fiscalyear_obj = Pool().get('account.fiscalyear')
-        fiscalyear_id = fiscalyear_obj.find(
-                Transaction().context.get('company'), exception=False)
-        if fiscalyear_id:
-            return fiscalyear_id
+    @staticmethod
+    def default_fiscalyear():
+        FiscalYear = Pool().get('account.fiscalyear')
+        return FiscalYear.find(
+            Transaction().context.get('company'), exception=False)
 
-    def default_balance_type(self):
+    @staticmethod
+    def default_balance_type():
         return "customer"
 
-    def default_posted(self):
+    @staticmethod
+    def default_posted():
         return False
 
-    def default_term1(self):
+    @staticmethod
+    def default_term1():
         return 30
 
-    def default_term2(self):
+    @staticmethod
+    def default_term2():
         return 60
 
-    def default_term3(self):
+    @staticmethod
+    def default_term3():
         return 90
 
-    def default_unit(self):
+    @staticmethod
+    def default_unit():
         return 'day'
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-OpenAgedBalanceStart()
-
 
 class OpenAgedBalance(Wizard):
     'Open Aged Party Balance'
-    _name = 'account.open_aged_balance'
-
+    __name__ = 'account.open_aged_balance'
     start = StateView('account.open_aged_balance.start',
         'account.open_aged_balance_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -2198,52 +2046,51 @@
             ])
     print_ = StateAction('account.report_aged_balance')
 
-    def __init__(self):
-        super(OpenAgedBalance, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(OpenAgedBalance, cls).__setup__()
+        cls._error_messages.update({
                 'warning': 'Warning',
                 'term_overlap_desc': 'You cannot define overlapping terms'})
 
-    def do_print_(self, session, action):
-        if not (session.start.term1 < session.start.term2
-                < session.start.term3):
+    def do_print_(self, action):
+        if not (self.start.term1 < self.start.term2 < self.start.term3):
             self.raise_user_error(error="warning",
-                    error_description="term_overlap_desc")
+                error_description="term_overlap_desc")
         data = {
-            'company': session.start.company.id,
-            'fiscalyear': session.start.fiscalyear.id,
-            'term1': session.start.term1,
-            'term2': session.start.term2,
-            'term3': session.start.term3,
-            'unit': session.start.unit,
-            'posted': session.start.posted,
-            'balance_type': session.start.balance_type,
+            'company': self.start.company.id,
+            'fiscalyear': self.start.fiscalyear.id,
+            'term1': self.start.term1,
+            'term2': self.start.term2,
+            'term3': self.start.term3,
+            'unit': self.start.unit,
+            'posted': self.start.posted,
+            'balance_type': self.start.balance_type,
             }
         return action, data
 
-    def transition_print_(self, session):
+    def transition_print_(self):
         return 'end'
 
-OpenAgedBalance()
-
 
 class AgedBalance(Report):
-    _name = 'account.aged_balance'
+    __name__ = 'account.aged_balance'
 
-    def parse(self, report, objects, data, localcontext):
+    @classmethod
+    def parse(cls, report, objects, data, localcontext):
         pool = Pool()
-        party_obj = pool.get('party.party')
-        move_line_obj = pool.get('account.move.line')
-        company_obj = pool.get('company.company')
-        date_obj = pool.get('ir.date')
+        Party = pool.get('party.party')
+        MoveLine = pool.get('account.move.line')
+        Company = pool.get('company.company')
+        Date = pool.get('ir.date')
         cursor = Transaction().cursor
 
-        company = company_obj.browse(data['company'])
+        company = Company(data['company'])
         localcontext['digits'] = company.currency.digits
         localcontext['fiscalyear'] = data['fiscalyear']
         localcontext['posted'] = data['posted']
         with Transaction().set_context(context=localcontext):
-            line_query, _ = move_line_obj.query_get()
+            line_query, _ = MoveLine.query_get()
 
         terms = (data['term1'], data['term2'], data['term3'])
         if data['unit'] == 'month':
@@ -2262,14 +2109,14 @@
             if position == 0:
                 term_query = '(l.maturity_date <= %s '\
                     'OR l.maturity_date IS NULL) '
-                term_args = (date_obj.today() +
+                term_args = (Date.today() +
                         datetime.timedelta(days=term * coef),)
             else:
                 term_query = '(l.maturity_date <= %s '\
                     'AND l.maturity_date > %s) '
                 term_args = (
-                    date_obj.today() + datetime.timedelta(days=term * coef),
-                    date_obj.today()
+                    Date.today() + datetime.timedelta(days=term * coef),
+                    Date.today()
                     + datetime.timedelta(days=terms[position - 1] * coef),
                     )
 
@@ -2293,10 +2140,9 @@
                 else:
                     res[party] = [(i[0] == position) and solde \
                             or Decimal("0.0") for i in enumerate(terms)]
-        party_ids = party_obj.search([
+        parties = Party.search([
             ('id', 'in', [k for k in res.iterkeys()]),
             ])
-        parties = party_obj.browse(party_ids)
 
         localcontext['main_title'] = data['balance_type']
         localcontext['unit'] = data['unit']
@@ -2313,7 +2159,5 @@
                 'amount2': res[p.id][2],
                 } for p in parties]
 
-        return super(AgedBalance, self).parse(report, objects, data,
+        return super(AgedBalance, cls).parse(report, objects, data,
             localcontext)
-
-AgedBalance()
diff -r f40675df5d4c trytond/modules/account/configuration.py
--- a/trytond/modules/account/configuration.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/configuration.py	Mon Sep 24 20:36:38 2012 +0200
@@ -5,12 +5,12 @@
 from trytond.transaction import Transaction
 from trytond.pyson import Eval
 
+__all__ = ['Configuration']
+
 
 class Configuration(ModelSingleton, ModelSQL, ModelView):
     'Account Configuration'
-    _name = 'account.configuration'
-    _description = __doc__
-
+    __name__ = 'account.configuration'
     default_account_receivable = fields.Function(fields.Many2One(
         'account.account', 'Default Account Receivable',
         domain=[
@@ -26,46 +26,44 @@
                 ]),
         'get_account', setter='set_account')
 
-    def get_account(self, ids, name):
-        property_obj = Pool().get('ir.property')
-        model_field_obj = Pool().get('ir.model.field')
+    def get_account(self, name):
+        pool = Pool()
+        Property = pool.get('ir.property')
+        ModelField = pool.get('ir.model.field')
         company_id = Transaction().context.get('company')
-        account_field_id, = model_field_obj.search([
+        account_field, = ModelField.search([
             ('model.model', '=', 'party.party'),
             ('name', '=', name[8:]),
             ], limit=1)
-        property_ids = property_obj.search([
-            ('field', '=', account_field_id),
+        properties = Property.search([
+            ('field', '=', account_field.id),
             ('res', '=', None),
             ('company', '=', company_id),
             ], limit=1)
-        if property_ids:
-            prop = property_obj.browse(property_ids[0])
-            value = int(prop.value.split(',')[1])
-        else:
-            value = None
-        return dict((x, value) for x in ids)
+        if properties:
+            prop, = properties
+            return prop.value.id
 
-    def set_account(self, ids, name, value):
-        property_obj = Pool().get('ir.property')
-        model_field_obj = Pool().get('ir.model.field')
+    @classmethod
+    def set_account(cls, configurations, name, value):
+        pool = Pool()
+        Property = pool.get('ir.property')
+        ModelField = pool.get('ir.model.field')
         company_id = Transaction().context.get('company')
-        account_field_id, = model_field_obj.search([
+        account_field, = ModelField.search([
             ('model.model', '=', 'party.party'),
             ('name', '=', name[8:]),
             ], limit=1)
-        property_ids = property_obj.search([
-            ('field', '=', account_field_id),
+        properties = Property.search([
+            ('field', '=', account_field.id),
             ('res', '=', None),
             ('company', '=', company_id),
             ])
         with Transaction().set_user(0):
-            property_obj.delete(property_ids)
+            Property.delete(properties)
             if value:
-                property_obj.create({
-                    'field': account_field_id,
+                Property.create({
+                    'field': account_field.id,
                     'value': 'account.account,%s' % value,
                     'company': company_id,
                     })
-
-Configuration()
diff -r f40675df5d4c trytond/modules/account/fiscalyear.py
--- a/trytond/modules/account/fiscalyear.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/fiscalyear.py	Mon Sep 24 20:36:38 2012 +0200
@@ -8,6 +8,8 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['FiscalYear', 'CloseFiscalYearStart', 'CloseFiscalYear']
+
 STATES = {
     'readonly': Eval('state') == 'close',
 }
@@ -16,9 +18,7 @@
 
 class FiscalYear(ModelSQL, ModelView):
     'Fiscal Year'
-    _name = 'account.fiscalyear'
-    _description = __doc__
-
+    __name__ = 'account.fiscalyear'
     name = fields.Char('Name', size=None, required=True, depends=DEPENDS)
     code = fields.Char('Code', size=None)
     start_date = fields.Date('Starting Date', required=True, states=STATES,
@@ -48,14 +48,15 @@
                 Eval('context', {}).get('company', 0)),
             ], select=True)
 
-    def __init__(self):
-        super(FiscalYear, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(FiscalYear, cls).__setup__()
+        cls._constraints += [
             ('check_dates', 'fiscalyear_overlaps'),
             ('check_post_move_sequence', 'different_post_move_sequence'),
         ]
-        self._order.insert(0, ('start_date', 'ASC'))
-        self._error_messages.update({
+        cls._order.insert(0, ('start_date', 'ASC'))
+        cls._error_messages.update({
             'change_post_move_sequence': 'You can not change ' \
                     'the post move sequence',
             'no_fiscalyear_date': 'No fiscal year defined for this date!',
@@ -70,7 +71,7 @@
             'reopen_error': 'You can not reopen a fiscal year until ' \
                     'there is more recent fiscal year closed!',
             })
-        self._buttons.update({
+        cls._buttons.update({
                 'create_period': {
                     'invisible': ((Eval('state') != 'open')
                         | Eval('periods', [0])),
@@ -87,70 +88,69 @@
                     },
                 })
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'open'
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def check_dates(self, ids):
+    def check_dates(self):
         cursor = Transaction().cursor
-        for fiscalyear in self.browse(ids):
-            cursor.execute('SELECT id ' \
-                    'FROM ' + self._table + ' ' \
-                    'WHERE ((start_date <= %s AND end_date >= %s) ' \
-                            'OR (start_date <= %s AND end_date >= %s) ' \
-                            'OR (start_date >= %s AND end_date <= %s)) ' \
-                        'AND company = %s ' \
-                        'AND id != %s',
-                    (fiscalyear.start_date, fiscalyear.start_date,
-                        fiscalyear.end_date, fiscalyear.end_date,
-                        fiscalyear.start_date, fiscalyear.end_date,
-                        fiscalyear.company.id, fiscalyear.id))
-            if cursor.fetchone():
-                return False
+        cursor.execute('SELECT id ' \
+                'FROM ' + self._table + ' ' \
+                'WHERE ((start_date <= %s AND end_date >= %s) ' \
+                        'OR (start_date <= %s AND end_date >= %s) ' \
+                        'OR (start_date >= %s AND end_date <= %s)) ' \
+                    'AND company = %s ' \
+                    'AND id != %s',
+                (self.start_date, self.start_date,
+                    self.end_date, self.end_date,
+                    self.start_date, self.end_date,
+                    self.company.id, self.id))
+        if cursor.fetchone():
+            return False
         return True
 
-    def check_post_move_sequence(self, ids):
-        for fiscalyear in self.browse(ids):
-            if self.search([
-                ('post_move_sequence', '=', fiscalyear.post_move_sequence.id),
-                ('id', '!=', fiscalyear.id),
-                ]):
-                return False
+    def check_post_move_sequence(self):
+        if self.search([
+                    ('post_move_sequence', '=', self.post_move_sequence.id),
+                    ('id', '!=', self.id),
+                    ]):
+            return False
         return True
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, fiscalyears, vals):
         if vals.get('post_move_sequence'):
-            for fiscalyear in self.browse(ids):
+            for fiscalyear in fiscalyears:
                 if fiscalyear.post_move_sequence and \
                         fiscalyear.post_move_sequence.id != \
                         vals['post_move_sequence']:
-                    self.raise_user_error('change_post_move_sequence')
+                    cls.raise_user_error('change_post_move_sequence')
         vals = vals.copy()
         if 'periods' in vals:
             operator = ['delete', 'unlink_all', 'unlink', 'create', 'write',
                     'add', 'set']
             vals['periods'].sort(
                 lambda x, y: cmp(operator.index(x[0]), operator.index(y[0])))
-        return super(FiscalYear, self).write(ids, vals)
+        super(FiscalYear, cls).write(fiscalyears, vals)
 
-    def delete(self, ids):
-        period_obj = Pool().get('account.period')
+    @classmethod
+    def delete(cls, fiscalyears):
+        Period = Pool().get('account.period')
+        Period.delete([p for f in fiscalyears for p in f.periods])
+        super(FiscalYear, cls).delete(fiscalyears)
 
-        period_ids = []
-        for fiscalyear in self.browse(ids):
-            period_ids.extend([x.id for x in fiscalyear.periods])
-        period_obj.delete(period_ids)
-        return super(FiscalYear, self).delete(ids)
-
+    @classmethod
     @ModelView.button
-    def create_period(self, ids, interval=1):
+    def create_period(cls, fiscalyears, interval=1):
         '''
         Create periods for the fiscal years with month interval
         '''
-        period_obj = Pool().get('account.period')
-        for fiscalyear in self.browse(ids):
+        Period = Pool().get('account.period')
+        for fiscalyear in fiscalyears:
             period_start_date = fiscalyear.start_date
             while period_start_date < fiscalyear.end_date:
                 period_end_date = period_start_date + \
@@ -161,7 +161,7 @@
                 name = datetime_strftime(period_start_date, '%Y-%m')
                 if name != datetime_strftime(period_end_date, '%Y-%m'):
                     name += ' - ' + datetime_strftime(period_end_date, '%Y-%m')
-                period_obj.create({
+                Period.create({
                     'name': name,
                     'start_date': period_start_date,
                     'end_date': period_end_date,
@@ -171,138 +171,132 @@
                     })
                 period_start_date = period_end_date + relativedelta(days=1)
 
+    @classmethod
     @ModelView.button
-    def create_period_3(self, ids):
+    def create_period_3(cls, fiscalyears):
         '''
         Create periods for the fiscal years with 3 months interval
         '''
-        self.create_period(ids, interval=3)
+        cls.create_period(fiscalyears, interval=3)
 
-    def find(self, company_id, date=None, exception=True):
+    @classmethod
+    def find(cls, company_id, date=None, exception=True):
         '''
         Return the fiscal year for the company_id
             at the date or the current date.
         If exception is set the function will raise an exception
             if any fiscal year is found.
         '''
-        date_obj = Pool().get('ir.date')
+        Date = Pool().get('ir.date')
 
         if not date:
-            date = date_obj.today()
-        ids = self.search([
+            date = Date.today()
+        fiscalyears = cls.search([
             ('start_date', '<=', date),
             ('end_date', '>=', date),
             ('company', '=', company_id),
             ], order=[('start_date', 'DESC')], limit=1)
-        if not ids:
+        if not fiscalyears:
             if exception:
-                self.raise_user_error('no_fiscalyear_date')
+                cls.raise_user_error('no_fiscalyear_date')
             else:
                 return None
-        return ids[0]
+        return fiscalyears[0].id
 
-    def _process_account(self, account, fiscalyear):
+    def _process_account(self, account):
         '''
         Process account for a fiscal year closed
+        '''
+        Currency = Pool().get('currency.currency')
+        Deferral = Pool().get('account.account.deferral')
 
-        :param account: a BrowseRecord of the account
-        :param fiscalyear: a BrowseRecord of the fiscal year closed
-        '''
-        currency_obj = Pool().get('currency.currency')
-        deferral_obj = Pool().get('account.account.deferral')
-
-        if account.kind == 'view':
+        if self.kind == 'view':
             return
-        if not account.deferral:
-            if not currency_obj.is_zero(fiscalyear.company.currency,
-                    account.balance):
+        if not self.deferral:
+            if not Currency.is_zero(self.company.currency, account.balance):
                 self.raise_user_error('account_balance_not_zero',
                         error_args=(account.rec_name,))
         else:
-            deferral_obj.create({
+            Deferral.create({
                 'account': account.id,
-                'fiscalyear': fiscalyear.id,
+                'fiscalyear': self.id,
                 'debit': account.debit,
                 'credit': account.credit,
                 })
 
+    @classmethod
     @ModelView.button
-    def close(self, ids):
+    def close(cls, fiscalyears):
         '''
         Close a fiscal year
         '''
-        period_obj = Pool().get('account.period')
-        account_obj = Pool().get('account.account')
+        pool = Pool()
+        Period = pool.get('account.period')
+        Account = pool.get('account.account')
 
-        for fiscalyear in self.browse(ids):
-            if self.search([
+        for fiscalyear in fiscalyears:
+            if cls.search([
                 ('end_date', '<=', fiscalyear.start_date),
                 ('state', '=', 'open'),
                 ('company', '=', fiscalyear.company.id),
                 ]):
-                self.raise_user_error('close_error')
+                cls.raise_user_error('close_error')
 
             #First close the fiscalyear to be sure
             #it will not have new period created between.
-            self.write(fiscalyear.id, {
+            cls.write([fiscalyear], {
                 'state': 'close',
                 })
-            period_ids = period_obj.search([
-                ('fiscalyear', '=', fiscalyear.id),
-                ])
-            period_obj.close(period_ids)
+            periods = Period.search([
+                    ('fiscalyear', '=', fiscalyear.id),
+                    ])
+            Period.close(periods)
 
             with Transaction().set_context(fiscalyear=fiscalyear.id,
                     date=None):
-                account_ids = account_obj.search([
-                    ('company', '=', fiscalyear.company.id),
-                    ])
-                accounts = account_obj.browse(account_ids)
+                accounts = Account.search([
+                        ('company', '=', fiscalyear.company.id),
+                        ])
             for account in accounts:
-                self._process_account(account, fiscalyear)
+                fiscalyear._process_account(account)
 
+    @classmethod
     @ModelView.button
-    def reopen(self, ids):
+    def reopen(cls, fiscalyears):
         '''
         Re-open a fiscal year
         '''
-        deferral_obj = Pool().get('account.account.deferral')
+        Deferral = Pool().get('account.account.deferral')
 
-        for fiscalyear in self.browse(ids):
-            if self.search([
+        for fiscalyear in fiscalyears:
+            if cls.search([
                 ('start_date', '>=', fiscalyear.end_date),
                 ('state', '=', 'close'),
                 ('company', '=', fiscalyear.company.id),
                 ]):
-                self.raise_user_error('reopen_error')
+                cls.raise_user_error('reopen_error')
 
-            deferral_ids = deferral_obj.search([
+            deferrals = Deferral.search([
                 ('fiscalyear', '=', fiscalyear.id),
                 ])
-            deferral_obj.delete(deferral_ids)
+            Deferral.delete(deferrals)
 
-            self.write(fiscalyear.id, {
+            cls.write([fiscalyear], {
                 'state': 'open',
                 })
 
-FiscalYear()
-
 
 class CloseFiscalYearStart(ModelView):
     'Close Fiscal Year'
-    _name = 'account.fiscalyear.close.start'
-    _description = __doc__
+    __name__ = 'account.fiscalyear.close.start'
     close_fiscalyear = fields.Many2One('account.fiscalyear',
             'Fiscal Year to close', required=True,
             domain=[('state', '!=', 'close')])
 
-CloseFiscalYearStart()
-
 
 class CloseFiscalYear(Wizard):
     'Close Fiscal Year'
-    _name = 'account.fiscalyear.close'
-
+    __name__ = 'account.fiscalyear.close'
     start = StateView('account.fiscalyear.close.start',
         'account.fiscalyear_close_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -310,9 +304,7 @@
             ])
     close = StateTransition()
 
-    def transition_close(self, session):
-        fiscalyear_obj = Pool().get('account.fiscalyear')
-        fiscalyear_obj.close([session.start.close_fiscalyear.id])
+    def transition_close(self):
+        Fiscalyear = Pool().get('account.fiscalyear')
+        Fiscalyear.close([self.start.close_fiscalyear])
         return 'end'
-
-CloseFiscalYear()
diff -r f40675df5d4c trytond/modules/account/journal.py
--- a/trytond/modules/account/journal.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/journal.py	Mon Sep 24 20:36:38 2012 +0200
@@ -7,6 +7,9 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['JournalType', 'JournalView', 'JournalViewColumn', 'Journal',
+    'JournalPeriod', 'CloseJournalPeriod', 'ReOpenJournalPeriod']
+
 STATES = {
     'readonly': Eval('state') == 'close',
 }
@@ -18,41 +21,36 @@
 }
 
 
-class Type(ModelSQL, ModelView):
+class JournalType(ModelSQL, ModelView):
     'Journal Type'
-    _name = 'account.journal.type'
-    _description = __doc__
+    __name__ = 'account.journal.type'
     name = fields.Char('Name', size=None, required=True, translate=True)
     code = fields.Char('Code', size=None, required=True)
 
-    def __init__(self):
-        super(Type, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(JournalType, cls).__setup__()
+        cls._sql_constraints += [
             ('code_uniq', 'UNIQUE(code)', 'The code must be unique!'),
-        ]
-        self._order.insert(0, ('code', 'ASC'))
+            ]
+        cls._order.insert(0, ('code', 'ASC'))
 
-Type()
 
-
-class View(ModelSQL, ModelView):
+class JournalView(ModelSQL, ModelView):
     'Journal View'
-    _name = 'account.journal.view'
-    _description = __doc__
+    __name__ = 'account.journal.view'
     name = fields.Char('Name', size=None, required=True)
     columns = fields.One2Many('account.journal.view.column', 'view', 'Columns')
 
-    def __init__(self):
-        super(View, self).__init__()
-        self._order.insert(0, ('name', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(JournalView, cls).__setup__()
+        cls._order.insert(0, ('name', 'ASC'))
 
-View()
 
-
-class Column(ModelSQL, ModelView):
+class JournalViewColumn(ModelSQL, ModelView):
     'Journal View Column'
-    _name = 'account.journal.view.column'
-    _description = __doc__
+    __name__ = 'account.journal.view.column'
     name = fields.Char('Name', size=None, required=True)
     field = fields.Many2One('ir.model.field', 'Field', required=True,
             domain=[('model.model', '=', 'account.move.line')])
@@ -63,33 +61,33 @@
     required = fields.Boolean('Required')
     readonly = fields.Boolean('Readonly')
 
-    def __init__(self):
-        super(Column, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(JournalViewColumn, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(Column, self).init(module_name)
+        super(JournalViewColumn, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_required(self):
+    @staticmethod
+    def default_required():
         return False
 
-    def default_readonly(self):
+    @staticmethod
+    def default_readonly():
         return False
 
-Column()
-
 
 class Journal(ModelSQL, ModelView):
     'Journal'
-    _name = 'account.journal'
-    _description = __doc__
-
+    __name__ = 'account.journal'
     name = fields.Char('Name', size=None, required=True, translate=True)
     code = fields.Char('Code', size=None)
     active = fields.Boolean('Active', select=True)
@@ -126,60 +124,63 @@
                 'invisible': ~Eval('context', {}).get('company', 0),
                 }, depends=['type', 'centralised']))
 
-    def __init__(self):
-        super(Journal, self).__init__()
-        self._order.insert(0, ('name', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(Journal, cls).__setup__()
+        cls._order.insert(0, ('name', 'ASC'))
 
-    def init(self, module_name):
-        super(Journal, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Journal, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 sequence Many2One change into Property
         if table.column_exist('sequence'):
-            property_obj = Pool().get('ir.property')
-            cursor.execute('SELECT id, sequence FROM "' + self._table + '"')
+            Property = Pool().get('ir.property')
+            cursor.execute('SELECT id, sequence FROM "' + cls._table + '"')
             with Transaction().set_user(0):
                 for journal_id, sequence_id in cursor.fetchall():
-                    property_obj.set('sequence', self._name,
+                    Property.set('sequence', cls._name,
                             journal_id, (sequence_id and
                                 'ir.sequence,' + str(sequence_id) or False))
             table.drop_column('sequence', exception=True)
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_centralised(self):
+    @staticmethod
+    def default_centralised():
         return False
 
-    def default_update_posted(self):
+    @staticmethod
+    def default_update_posted():
         return False
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return None
 
-    def get_types(self):
-        type_obj = Pool().get('account.journal.type')
-        type_ids = type_obj.search([])
-        types = type_obj.browse(type_ids)
+    @staticmethod
+    def get_types():
+        Type = Pool().get('account.journal.type')
+        types = Type.search([])
         return [(x.code, x.name) for x in types]
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([
-            ('code',) + clause[1:],
-            ], limit=1, order=[])
-        if ids:
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        journals = cls.search([
+                ('code',) + clause[1:],
+                ], limit=1, order=[])
+        if journals:
             return [('code',) + clause[1:]]
-        return [(self._rec_name,) + clause[1:]]
+        return [(cls._rec_name,) + clause[1:]]
 
-Journal()
 
-
-class Period(ModelSQL, ModelView):
+class JournalPeriod(ModelSQL, ModelView):
     'Journal - Period'
-    _name = 'account.journal.period'
-    _description = __doc__
-
+    __name__ = 'account.journal.period'
     name = fields.Char('Name', size=None, required=True)
     journal = fields.Many2One('account.journal', 'Journal', required=True,
             ondelete='CASCADE', states=STATES, depends=DEPENDS)
@@ -193,14 +194,15 @@
         ('close', 'Close'),
         ], 'State', readonly=True, required=True)
 
-    def __init__(self):
-        super(Period, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(JournalPeriod, cls).__setup__()
+        cls._sql_constraints += [
             ('journal_period_uniq', 'UNIQUE(journal, period)',
                 'You can only open one journal per period!'),
-        ]
-        self._order.insert(0, ('name', 'ASC'))
-        self._error_messages.update({
+            ]
+        cls._order.insert(0, ('name', 'ASC'))
+        cls._error_messages.update({
             'modify_del_journal_period': 'You can not modify/delete ' \
                     'a journal - period with moves!',
             'create_journal_period': 'You can not create ' \
@@ -209,93 +211,89 @@
                     'a journal - period from a closed period!',
             })
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'open'
 
-    def get_icon(self, ids, name):
-        res = {}
-        for period in self.browse(ids):
-            res[period.id] = _ICONS.get(period.state, '')
-        return res
+    def get_icon(self, name):
+        return _ICONS.get(self.state, '')
 
-    def _check(self, ids):
-        move_obj = Pool().get('account.move')
-        for period in self.browse(ids):
-            move_ids = move_obj.search([
-                ('journal', '=', period.journal.id),
-                ('period', '=', period.period.id),
-                ], limit=1)
-            if move_ids:
-                self.raise_user_error('modify_del_journal_period')
-        return
+    @classmethod
+    def _check(cls, periods):
+        Move = Pool().get('account.move')
+        for period in periods:
+            moves = Move.search([
+                    ('journal', '=', period.journal.id),
+                    ('period', '=', period.period.id),
+                    ], limit=1)
+            if moves:
+                cls.raise_user_error('modify_del_journal_period')
 
-    def create(self, vals):
-        period_obj = Pool().get('account.period')
+    @classmethod
+    def create(cls, vals):
+        Period = Pool().get('account.period')
         if vals.get('period'):
-            period = period_obj.browse(vals['period'])
+            period = Period(vals['period'])
             if period.state == 'close':
-                self.raise_user_error('create_journal_period')
-        return super(Period, self).create(vals)
+                cls.raise_user_error('create_journal_period')
+        return super(JournalPeriod, cls).create(vals)
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, periods, vals):
         if vals != {'state': 'close'} \
                 and vals != {'state': 'open'}:
-            self._check(ids)
+            cls._check(periods)
         if vals.get('state') == 'open':
-            for journal_period in self.browse(ids):
+            for journal_period in periods:
                 if journal_period.period.state == 'close':
-                    self.raise_user_error('open_journal_period')
-        return super(Period, self).write(ids, vals)
+                    cls.raise_user_error('open_journal_period')
+        super(JournalPeriod, cls).write(periods, vals)
 
-    def delete(self, ids):
-        self._check(ids)
-        return super(Period, self).delete(ids)
+    @classmethod
+    def delete(cls, periods):
+        cls._check(periods)
+        super(JournalPeriod, cls).delete(periods)
 
-    def close(self, ids):
+    @classmethod
+    def close(cls, periods):
         '''
         Close journal - period
+        '''
+        cls.write(periods, {
+                'state': 'close',
+                })
 
-        :param ids: the journal - period ids
-        '''
-        self.write(ids, {
-            'state': 'close',
-            })
-
-    def open_(self, ids):
+    @classmethod
+    def open_(cls, periods):
         "Open journal - period"
-        self.write(ids, {
+        cls.write(periods, {
                 'state': 'open',
                 })
 
-Period()
 
-
-class ClosePeriod(Wizard):
+class CloseJournalPeriod(Wizard):
     'Close Journal - Period'
-    _name = 'account.journal.period.close'
+    __name__ = 'account.journal.period.close'
     start_state = 'close'
     close = StateTransition()
 
-    def transition_close(self, session):
-        journal_period_obj = Pool().get('account.journal.period')
-        journal_period_obj.close(Transaction().context['active_ids'])
+    def transition_close(self):
+        JournalPeriod = Pool().get('account.journal.period')
+        JournalPeriod.close(JournalPeriod(Transaction().context['active_ids']))
         return 'end'
 
-ClosePeriod()
 
-
-class ReOpenPeriod(Wizard):
+class ReOpenJournalPeriod(Wizard):
     'Re-Open Journal - Period'
-    _name = 'account.journal.period.reopen'
+    __name__ = 'account.journal.period.reopen'
     start_state = 'reopen'
     reopen = StateTransition()
 
-    def transition_reopen(self, session):
-        journal_period_obj = Pool().get('account.journal.period')
-        journal_period_obj.open_(Transaction().context['active_ids'])
+    def transition_reopen(self):
+        JournalPeriod = Pool().get('account.journal.period')
+        JournalPeriod.open_(JournalPeriod(Transaction().context['active_ids']))
         return 'end'
-
-ReOpenPeriod()
diff -r f40675df5d4c trytond/modules/account/move.py
--- a/trytond/modules/account/move.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/move.py	Mon Sep 24 20:36:38 2012 +0200
@@ -11,8 +11,16 @@
 from trytond.backend import TableHandler, FIELDS
 from trytond.pyson import Eval, PYSONEncoder
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.config import CONFIG
+from trytond.rpc import RPC
+
+__all__ = ['Move', 'Reconciliation', 'Line', 'Move2', 'OpenJournalAsk',
+    'OpenJournal', 'OpenAccount', 'ReconcileLinesWriteOff', 'ReconcileLines',
+    'UnreconcileLinesStart', 'UnreconcileLines', 'OpenReconcileLinesStart',
+    'OpenReconcileLines', 'FiscalYearLine', 'FiscalYear2',
+    'PrintGeneralJournalStart', 'PrintGeneralJournal', 'GeneralJournal']
+__metaclass__ = PoolMeta
 
 _MOVE_STATES = {
     'readonly': Eval('state') == 'posted',
@@ -26,9 +34,7 @@
 
 class Move(ModelSQL, ModelView):
     'Account Move'
-    _name = 'account.move'
-    _description = __doc__
-
+    __name__ = 'account.move'
     name = fields.Char('Name', size=None, required=True, states=_MOVE_STATES,
             depends=_MOVE_DEPENDS)
     reference = fields.Char('Reference', size=None, readonly=True,
@@ -52,17 +58,18 @@
                 'date': Eval('date'),
             })
 
-    def __init__(self):
-        super(Move, self).__init__()
-        self._check_modify_exclude = ['state']
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Move, cls).__setup__()
+        cls._check_modify_exclude = ['state']
+        cls._constraints += [
             ('check_centralisation', 'period_centralized_journal'),
             ('check_company', 'company_in_move'),
             ('check_date', 'date_outside_period'),
         ]
-        self._order.insert(0, ('date', 'DESC'))
-        self._order.insert(1, ('reference', 'DESC'))
-        self._error_messages.update({
+        cls._order.insert(0, ('date', 'DESC'))
+        cls._order.insert(1, ('reference', 'DESC'))
+        cls._error_messages.update({
             'del_posted_move': 'You can not delete posted moves!',
             'post_empty_move': 'You can not post an empty move!',
             'post_unbalanced_move': 'You can not post an unbalanced move!',
@@ -80,7 +87,7 @@
             'draft_closed_period': 'You can not set to draft a move ' \
                     'from a closed period',
             })
-        self._buttons.update({
+        cls._buttons.update({
                 'post': {
                     'invisible': Eval('state') == 'posted',
                     },
@@ -89,184 +96,166 @@
                     },
                 })
 
-    def init(self, module_name):
-        super(Move, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Move, cls).__register__(module_name)
 
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action(['journal', 'period'], 'add')
 
         # Add index on create_date
         table.index_action('create_date', action='add')
 
-    def default_period(self):
-        period_obj = Pool().get('account.period')
-        return period_obj.find(Transaction().context.get('company'),
-                exception=False)
+    @staticmethod
+    def default_period():
+        Period = Pool().get('account.period')
+        return Period.find(Transaction().context.get('company'),
+            exception=False)
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_date(self):
-        period_obj = Pool().get('account.period')
-        date_obj = Pool().get('ir.date')
-        period_id = self.default_period()
+    @classmethod
+    def default_date(cls):
+        pool = Pool()
+        Period = pool.get('account.period')
+        Date = pool.get('ir.date')
+        period_id = cls.default_period()
         if period_id:
-            period = period_obj.browse(period_id)
+            period = Period(period_id)
             return period.start_date
-        return date_obj.today()
+        return Date.today()
 
-    def on_change_with_date(self, vals):
-        line_obj = Pool().get('account.move.line')
-        period_obj = Pool().get('account.period')
-        res = vals['date']
-        line_ids = line_obj.search([
-            ('journal', '=', vals.get('journal')),
-            ('period', '=', vals.get('period')),
-            ], order=[('id', 'DESC')], limit=1)
-        if line_ids:
-            line = line_obj.browse(line_ids[0])
-            res = line.date
-        elif vals.get('period'):
-            period = period_obj.browse(vals['period'])
-            res = period.start_date
-        return res
+    def on_change_with_date(self):
+        Line = Pool().get('account.move.line')
+        date = self.date
+        lines = Line.search([
+                ('journal', '=', self.journal),
+                ('period', '=', self.period),
+                ], order=[('id', 'DESC')], limit=1)
+        if lines:
+            date = lines[0].date
+        elif self.period:
+            date = self.period.start_date
+        return date
 
-    def check_centralisation(self, ids):
-        for move in self.browse(ids):
-            if move.journal.centralised:
-                move_ids = self.search([
-                    ('period', '=', move.period.id),
-                    ('journal', '=', move.journal.id),
+    def check_centralisation(self):
+        if self.journal.centralised:
+            moves = self.search([
+                    ('period', '=', self.period.id),
+                    ('journal', '=', self.journal.id),
                     ('state', '!=', 'posted'),
                     ], limit=2)
-                if len(move_ids) > 1:
-                    return False
-        return True
-
-    def check_company(self, ids):
-        for move in self.browse(ids):
-            company_id = -1
-            for line in move.lines:
-                if company_id < 0:
-                    company_id = line.account.company.id
-                if line.account.company.id != company_id:
-                    return False
-        return True
-
-    def check_date(self, ids):
-        for move in self.browse(ids):
-            if move.date < move.period.start_date:
-                return False
-            if move.date > move.period.end_date:
+            if len(moves) > 1:
                 return False
         return True
 
-    def check_modify(self, ids):
+    def check_company(self):
+        company_id = -1
+        for line in self.lines:
+            if company_id < 0:
+                company_id = line.account.company.id
+            if line.account.company.id != company_id:
+                return False
+        return True
+
+    def check_date(self):
+        return self.period.start_date <= self.date <= self.period.end_date
+
+    @classmethod
+    def check_modify(cls, moves):
         'Check posted moves for modifications.'
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for move in self.browse(ids):
+        for move in moves:
             if move.state == 'posted':
-                self.raise_user_error('modify_posted_move',
+                cls.raise_user_error('modify_posted_move',
                     error_args=(move.name,))
-        return
 
-    def search_rec_name(self, name, clause):
-        ids = self.search(['OR',
-            ('reference',) + tuple(clause[1:]),
-            (self._rec_name,) + tuple(clause[1:]),
-            ])
-        return [('id', 'in', ids)]
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        moves = cls.search(['OR',
+                ('reference',) + tuple(clause[1:]),
+                (cls._rec_name,) + tuple(clause[1:]),
+                ])
+        return [('id', 'in', [m.id for m in moves])]
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, moves, vals):
         keys = vals.keys()
-        for key in self._check_modify_exclude:
+        for key in cls._check_modify_exclude:
             if key in keys:
                 keys.remove(key)
         if len(keys):
-            self.check_modify(ids)
-        res = super(Move, self).write(ids, vals)
-        self.validate(ids)
-        return res
+            cls.check_modify(moves)
+        super(Move, cls).write(moves, vals)
+        cls.validate(moves)
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         pool = Pool()
-        move_line_obj = pool.get('account.move.line')
-        sequence_obj = pool.get('ir.sequence')
-        journal_obj = pool.get('account.journal')
+        MoveLine = pool.get('account.move.line')
+        Sequence = pool.get('ir.sequence')
+        Journal = pool.get('account.journal')
 
         if not vals.get('name'):
             journal_id = (vals.get('journal')
                     or Transaction().context.get('journal'))
             if journal_id:
                 vals = vals.copy()
-                journal = journal_obj.browse(journal_id)
-                vals['name'] = sequence_obj.get_id(journal.sequence.id)
+                journal = Journal(journal_id)
+                vals['name'] = Sequence.get_id(journal.sequence.id)
 
-        res = super(Move, self).create(vals)
-        move = self.browse(res)
+        move = super(Move, cls).create(vals)
         if move.journal.centralised:
-            line_id = move_line_obj.create({
-                'account': move.journal.credit_account.id,
-                'move': move.id,
-                'name': 'Centralised Counterpart',
-                })
-            self.write(move.id, {
-                'centralised_line': line_id,
-                })
+            line = MoveLine.create({
+                    'account': move.journal.credit_account.id,
+                    'move': move.id,
+                    'name': 'Centralised Counterpart',
+                    })
+            cls.write([move], {
+                    'centralised_line': line.id,
+                    })
         if 'lines' in vals:
-            self.validate([res])
-        return res
+            cls.validate([move])
+        return move
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        move_line_obj = Pool().get('account.move.line')
-        self.check_modify(ids)
-        for move in self.browse(ids):
-            if move.lines:
-                move_line_ids = [x.id for x in move.lines]
-                move_line_obj.delete(move_line_ids)
-        return super(Move, self).delete(ids)
+    @classmethod
+    def delete(cls, moves):
+        MoveLine = Pool().get('account.move.line')
+        cls.check_modify(moves)
+        MoveLine.delete([l for m in moves for l in m.lines])
+        super(Move, cls).delete(moves)
 
-    def copy(self, ids, default=None):
-        line_obj = Pool().get('account.move.line')
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+    @classmethod
+    def copy(cls, moves, default=None):
+        Line = Pool().get('account.move.line')
 
         if default is None:
             default = {}
         default = default.copy()
         default['reference'] = None
-        default['state'] = self.default_state()
+        default['state'] = cls.default_state()
         default['post_date'] = None
         default['lines'] = None
 
-        new_ids = []
-        for move in self.browse(ids):
-            new_id = super(Move, self).copy(move.id, default=default)
-            line_obj.copy([x.id for x in move.lines], default={
-                'move': new_id,
-                })
-            new_ids.append(new_id)
+        new_moves = []
+        for move in moves:
+            new_move, = super(Move, cls).copy([move], default=default)
+            Line.copy(move.lines, default={
+                    'move': new_move.id,
+                    })
+            new_moves.append(new_move)
+        return new_moves
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-    def validate(self, ids):
+    @classmethod
+    def validate(cls, moves):
         '''
         Validate balanced move and centralise it if in centralised journal
         '''
-        currency_obj = Pool().get('currency.currency')
-        move_line_obj = Pool().get('account.move.line')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for move in self.browse(ids):
+        pool = Pool()
+        MoveLine = pool.get('account.move.line')
+        for move in moves:
             if not move.lines:
                 continue
             amount = Decimal('0.0')
@@ -278,10 +267,10 @@
                     company = line.account.company
                 if line.state == 'draft':
                     draft_lines.append(line)
-            if not currency_obj.is_zero(company.currency, amount):
+            if not company.currency.is_zero(amount):
                 if not move.journal.centralised:
-                    move_line_obj.write(
-                            [x.id for x in move.lines if x.state != 'draft'], {
+                    MoveLine.write(
+                            [x for x in move.lines if x.state != 'draft'], {
                                 'state': 'draft',
                                 })
                 else:
@@ -300,18 +289,18 @@
                         credit = - centralised_amount
                         account_id = move.journal.credit_account.id
                     if not move.centralised_line:
-                        centralised_line_id = move_line_obj.create({
+                        centralised_line = MoveLine.create({
                             'debit': debit,
                             'credit': credit,
                             'account': account_id,
                             'move': move.id,
                             'name': 'Centralised Counterpart',
                             })
-                        self.write(move.id, {
-                            'centralised_line': centralised_line_id,
+                        cls.write([move], {
+                            'centralised_line': centralised_line.id,
                             })
                     else:
-                        move_line_obj.write(move.centralised_line.id, {
+                        MoveLine.write([move.centralised_line], {
                                 'debit': debit,
                                 'credit': credit,
                                 'account': account_id,
@@ -319,71 +308,63 @@
                 continue
             if not draft_lines:
                 continue
-            move_line_obj.write([x.id for x in draft_lines], {
+            MoveLine.write(draft_lines, {
                     'state': 'valid',
                     })
-        return
 
+    @classmethod
     @ModelView.button
-    def post(self, ids):
+    def post(cls, moves):
         pool = Pool()
-        currency_obj = pool.get('currency.currency')
-        sequence_obj = pool.get('ir.sequence')
-        date_obj = pool.get('ir.date')
+        Sequence = pool.get('ir.sequence')
+        Date = pool.get('ir.date')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        moves = self.browse(ids)
         for move in moves:
             amount = Decimal('0.0')
             if not move.lines:
-                self.raise_user_error('post_empty_move')
+                cls.raise_user_error('post_empty_move')
             company = None
             for line in move.lines:
                 amount += line.debit - line.credit
                 if not company:
                     company = line.account.company
-            if not currency_obj.is_zero(company.currency, amount):
-                self.raise_user_error('post_unbalanced_move')
+            if not company.currency.is_zero(amount):
+                cls.raise_user_error('post_unbalanced_move')
         for move in moves:
-            reference = sequence_obj.get_id(move.period.post_move_sequence.id)
-            self.write(move.id, {
+            reference = Sequence.get_id(move.period.post_move_sequence.id)
+            cls.write([move], {
                 'reference': reference,
                 'state': 'posted',
-                'post_date': date_obj.today(),
+                'post_date': Date.today(),
                 })
-        return
 
+    @classmethod
     @ModelView.button
-    def draft(self, ids):
-        for move in self.browse(ids):
+    def draft(cls, moves):
+        for move in moves:
             if not move.journal.update_posted:
-                self.raise_user_error('draft_posted_move_journal')
+                cls.raise_user_error('draft_posted_move_journal')
             if move.period.state == 'close':
-                self.raise_user_error('draft_closed_period')
-        return self.write(ids, {
+                cls.raise_user_error('draft_closed_period')
+        return cls.write(moves, {
             'state': 'draft',
             })
 
-Move()
-
 
 class Reconciliation(ModelSQL, ModelView):
     'Account Move Reconciliation Lines'
-    _name = 'account.move.reconciliation'
-    _description = __doc__
-
+    __name__ = 'account.move.reconciliation'
     name = fields.Char('Name', size=None, required=True)
     lines = fields.One2Many('account.move.line', 'reconciliation',
             'Lines')
 
-    def __init__(self):
-        super(Reconciliation, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Reconciliation, cls).__setup__()
+        cls._constraints += [
             ('check_lines', 'invalid_reconciliation'),
-        ]
-        self._error_messages.update({
+            ]
+        cls._error_messages.update({
             'modify': 'You can not modify a reconciliation!',
             'invalid_reconciliation': 'You can not create reconciliation ' \
                     'where lines are not balanced, nor valid, ' \
@@ -391,21 +372,23 @@
                     'nor from the same party!',
             })
 
-    def create(self, vals):
-        sequence_obj = Pool().get('ir.sequence')
+    @classmethod
+    def create(cls, vals):
+        Sequence = Pool().get('ir.sequence')
 
         if 'name' not in vals:
             vals = vals.copy()
-            vals['name'] = sequence_obj.get('account.move.reconciliation')
+            vals['name'] = Sequence.get('account.move.reconciliation')
 
-        return super(Reconciliation, self).create(vals)
+        return super(Reconciliation, cls).create(vals)
 
-    def write(self, ids, vals):
-        self.raise_user_error('modify')
+    @classmethod
+    def write(cls, moves, vals):
+        cls.raise_user_error('modify')
 
-    def check_lines(self, ids):
-        currency_obj = Pool().get('currency.currency')
-        for reconciliation in self.browse(ids):
+    @classmethod
+    def check_lines(cls, reconciliations):
+        for reconciliation in reconciliations:
             amount = Decimal('0.0')
             account = None
             party = None
@@ -423,18 +406,14 @@
                     party = line.party
                 elif line.party and party.id != line.party.id:
                     return False
-            if not currency_obj.is_zero(account.company.currency, amount):
+            if not account.company.currency.is_zero(amount):
                 return False
         return True
 
-Reconciliation()
-
 
 class Line(ModelSQL, ModelView):
     'Account Move Line'
-    _name = 'account.move.line'
-    _description = __doc__
-
+    __name__ = 'account.move.line'
     name = fields.Char('Name', size=None, required=True)
     debit = fields.Numeric('Debit', digits=(16, Eval('currency_digits', 2)),
         required=True,
@@ -497,21 +476,22 @@
     second_currency_digits = fields.Function(fields.Integer(
         'Second Currency Digits'), 'get_currency_digits')
 
-    def __init__(self):
-        super(Line, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Line, cls).__setup__()
+        cls._sql_constraints += [
             ('credit_debit',
                 'CHECK(credit * debit = 0.0)',
                 'Wrong credit/debit values!'),
-        ]
-        self._constraints += [
+            ]
+        cls._constraints += [
             ('check_account', 'move_view_inactive_account'),
-        ]
-        self._rpc.update({
-            'on_write': False,
-        })
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+            ]
+        cls.__rpc__.update({
+                'on_write': RPC(instantiate=0),
+                })
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
             'add_modify_closed_journal_period': 'You can not ' \
                     'add/modify lines in a closed journal period!',
             'modify_posted_move': 'You can not modify lines of a posted move!',
@@ -522,64 +502,71 @@
             'already_reconciled': 'Line "%s" (%d) already reconciled!',
             })
 
-    def init(self, module_name):
-        super(Line, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Line, cls).__register__(module_name)
 
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         # Index for General Ledger
         table.index_action(['move', 'account'], 'add')
 
         # Migration from 1.2
         table.not_null_action('blocked', action='remove')
 
-    def default_date(self):
+    @classmethod
+    def default_date(cls):
         '''
         Return the date of the last line for journal, period
         or the starting date of the period
         or today
         '''
-        period_obj = Pool().get('account.period')
-        date_obj = Pool().get('ir.date')
+        pool = Pool()
+        Period = pool.get('account.period')
+        Date = pool.get('ir.date')
 
-        res = date_obj.today()
-        line_ids = self.search([
-            ('journal', '=', Transaction().context.get('journal')),
-            ('period', '=', Transaction().context.get('period')),
-            ], order=[('id', 'DESC')], limit=1)
-        if line_ids:
-            line = self.browse(line_ids[0])
-            res = line.date
+        date = Date.today()
+        lines = cls.search([
+                ('journal', '=', Transaction().context.get('journal')),
+                ('period', '=', Transaction().context.get('period')),
+                ], order=[('id', 'DESC')], limit=1)
+        if lines:
+            date = lines[0].date
         elif Transaction().context.get('period'):
-            period = period_obj.browse(Transaction().context['period'])
-            res = period.start_date
+            period = Period(Transaction().context['period'])
+            date = period.start_date
         if Transaction().context.get('date'):
-            res = Transaction().context['date']
-        return res
+            date = Transaction().context['date']
+        return date
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_currency_digits(self):
+    @staticmethod
+    def default_currency_digits():
         return 2
 
-    def default_debit(self):
+    @staticmethod
+    def default_debit():
         return Decimal(0)
 
-    def default_credit(self):
+    @staticmethod
+    def default_credit():
         return Decimal(0)
 
-    def default_get(self, fields, with_rec_name=True):
+    @classmethod
+    def default_get(cls, fields, with_rec_name=True):
         pool = Pool()
-        move_obj = pool.get('account.move')
-        tax_obj = pool.get('account.tax')
-        account_obj = pool.get('account.account')
-        tax_code_obj = pool.get('account.tax.code')
-        currency_obj = pool.get('currency.currency')
-        values = super(Line, self).default_get(fields,
+        Move = pool.get('account.move')
+        Tax = pool.get('account.tax')
+        Account = pool.get('account.account')
+        TaxCode = pool.get('account.tax.code')
+        values = super(Line, cls).default_get(fields,
                 with_rec_name=with_rec_name)
 
         if 'move' not in fields:
@@ -591,21 +578,20 @@
 
         if (Transaction().context.get('journal')
                 and Transaction().context.get('period')):
-            line_ids = self.search([
+            lines = cls.search([
                 ('move.journal', '=', Transaction().context['journal']),
                 ('move.period', '=', Transaction().context['period']),
                 ('create_uid', '=', Transaction().user),
                 ('state', '=', 'draft'),
                 ], order=[('id', 'DESC')], limit=1)
-            if not line_ids:
+            if not lines:
                 return values
-            line = self.browse(line_ids[0])
-            values['move'] = line.move.id
+            values['move'] = lines[0].move.id
 
         if 'move' not in values:
             return values
 
-        move = move_obj.browse(values['move'])
+        move = Move(values['move'])
         total = Decimal('0.0')
         taxes = {}
         no_code_taxes = []
@@ -682,8 +668,7 @@
                             key = 'credit_note'
                     line_amount = Decimal('0.0')
                     tax_amount = Decimal('0.0')
-                    for tax_line in tax_obj.compute(
-                            [x.id for x in line.account.taxes],
+                    for tax_line in Tax.compute(line.account.taxes,
                             line.debit or line.credit, 1):
                         if (tax_line['tax'][key + '_account'].id \
                                 or line.account.id) == account_id \
@@ -696,10 +681,10 @@
                                 line_amount -= tax_line['amount']
                             tax_amount += tax_line['amount'] * \
                                     tax_line['tax'][key + '_tax_sign']
-                    line_amount = currency_obj.round(
-                            line.account.company.currency, line_amount)
-                    tax_amount = currency_obj.round(
-                            line.account.company.currency, tax_amount)
+                    line_amount = line.account.company.currency.round(
+                        line_amount)
+                    tax_amount = line.account.company.currency.round(
+                        tax_amount)
                     if ('debit' in fields):
                         values['debit'] = line_amount > Decimal('0.0') \
                                 and line_amount or Decimal('0.0')
@@ -708,102 +693,96 @@
                                 and - line_amount or Decimal('0.0')
                     if 'account' in fields:
                         values['account'] = account_id
-                        values['account.rec_name'] = account_obj.browse(
-                                account_id).rec_name
+                        values['account.rec_name'] = Account(
+                            account_id).rec_name
                     if 'tax_lines' in fields and code_id:
                         values['tax_lines'] = [
                             {
                                 'amount': tax_amount,
                                 'currency_digits': line.currency_digits,
                                 'code': code_id,
-                                'code.rec_name': tax_code_obj.browse(
-                                    code_id).rec_name,
+                                'code.rec_name': TaxCode(code_id).rec_name,
                                 'tax': tax_id,
-                                'tax.rec_name': tax_obj.browse(tax_id).rec_name
+                                'tax.rec_name': Tax(tax_id).rec_name,
                             },
                         ]
         return values
 
-    def get_currency_digits(self, ids, names):
-        res = {}
-        for line in self.browse(ids):
+    @classmethod
+    def get_currency_digits(cls, lines, names):
+        digits = {}
+        for line in lines:
             for name in names:
-                res.setdefault(name, {})
-                res[name].setdefault(line.id, 2)
+                digits.setdefault(name, {})
+                digits[name].setdefault(line.id, 2)
                 if name == 'currency_digits':
-                    res[name][line.id] = line.account.currency_digits
+                    digits[name][line.id] = line.account.currency_digits
                 elif name == 'second_currency_digits':
                     second_currency = line.account.second_currency
                     if second_currency:
-                        res[name][line.id] = second_currency.digits
-        return res
+                        digits[name][line.id] = second_currency.digits
+        return digits
 
-    def on_change_debit(self, vals):
-        res = {}
-        journal_obj = Pool().get('account.journal')
-        if vals.get('journal') or Transaction().context.get('journal'):
-            journal = journal_obj.browse(vals.get('journal')
-                    or Transaction().context.get('journal'))
+    def on_change_debit(self):
+        changes = {}
+        Journal = Pool().get('account.journal')
+        if self.journal or Transaction().context.get('journal'):
+            journal = self.journal or Journal(Transaction().context['journal'])
             if journal.type in ('expense', 'revenue'):
-                res['tax_lines'] = self._compute_tax_lines(vals, journal.type)
-                if not res['tax_lines']:
-                    del res['tax_lines']
-        if vals.get('debit'):
-            res['credit'] = Decimal('0.0')
-        return res
+                changes['tax_lines'] = self._compute_tax_lines(journal.type)
+                if not changes['tax_lines']:
+                    del changes['tax_lines']
+        if self.debit:
+            changes['credit'] = Decimal('0.0')
+        return changes
 
-    def on_change_credit(self, vals):
-        res = {}
-        journal_obj = Pool().get('account.journal')
-        if vals.get('journal') or Transaction().context.get('journal'):
-            journal = journal_obj.browse(vals.get('journal')
-                    or Transaction().context.get('journal'))
+    def on_change_credit(self):
+        changes = {}
+        Journal = Pool().get('account.journal')
+        if self.journal or Transaction().context.get('journal'):
+            journal = self.journal or Journal(Transaction().context['journal'])
             if journal.type in ('expense', 'revenue'):
-                res['tax_lines'] = self._compute_tax_lines(
-                        vals, journal.type)
-                if not res['tax_lines']:
-                    del res['tax_lines']
-        if vals.get('credit'):
-            res['debit'] = Decimal('0.0')
-        return res
+                changes['tax_lines'] = self._compute_tax_lines(journal.type)
+                if not changes['tax_lines']:
+                    del changes['tax_lines']
+        if self.credit:
+            changes['debit'] = Decimal('0.0')
+        return changes
 
-    def on_change_account(self, vals):
-        account_obj = Pool().get('account.account')
-        journal_obj = Pool().get('account.journal')
+    def on_change_account(self):
+        Journal = Pool().get('account.journal')
 
-        res = {}
+        changes = {}
         if Transaction().context.get('journal'):
-            journal = journal_obj.browse(Transaction().context['journal'])
+            journal = Journal(Transaction().context['journal'])
             if journal.type in ('expense', 'revenue'):
-                res['tax_lines'] = self._compute_tax_lines(vals, journal.type)
-                if not res['tax_lines']:
-                    del res['tax_lines']
+                changes['tax_lines'] = self._compute_tax_lines(journal.type)
+                if not changes['tax_lines']:
+                    del changes['tax_lines']
 
-        if vals.get('account'):
-            account = account_obj.browse(vals['account'])
-            res['currency_digits'] = account.currency_digits
-            if account.second_currency:
-                res['second_currency_digits'] = account.second_currency.digits
-        return res
+        if self.account:
+            changes['currency_digits'] = self.account.currency_digits
+            if self.account.second_currency:
+                changes['second_currency_digits'] = \
+                    self.account.second_currency.digits
+        return changes
 
-    def _compute_tax_lines(self, vals, journal_type):
+    def _compute_tax_lines(self, journal_type):
         res = {}
         pool = Pool()
-        account_obj = pool.get('account.account')
-        tax_code_obj = pool.get('account.tax.code')
-        tax_obj = pool.get('account.tax')
-        tax_line_obj = pool.get('account.tax.line')
+        TaxCode = pool.get('account.tax.code')
+        Tax = pool.get('account.tax')
+        TaxLine = pool.get('account.tax.line')
 
-        if vals.get('move'):
+        if self.move:
             #Only for first line
             return res
-        if vals.get('tax_lines'):
-            res['remove'] = [x['id'] for x in vals['tax_lines']]
-        if vals.get('account'):
-            account = account_obj.browse(vals['account'])
-            debit = vals.get('debit', Decimal('0.0'))
-            credit = vals.get('credit', Decimal('0.0'))
-            for tax in account.taxes:
+        if self.tax_lines:
+            res['remove'] = [x['id'] for x in self.tax_lines]
+        if self.account:
+            debit = self.debit or Decimal('0.0')
+            credit = self.credit or  Decimal('0.0')
+            for tax in self.account.taxes:
                 if journal_type == 'revenue':
                     if debit:
                         key = 'credit_note'
@@ -815,8 +794,7 @@
                     else:
                         key = 'credit_note'
                 base_amounts = {}
-                for tax_line in tax_obj.compute(
-                        [x.id for x in account.taxes],
+                for tax_line in Tax.compute(self.account.taxes,
                         debit or credit, 1):
                     code_id = tax_line['tax'][key + '_base_code'].id
                     if not code_id:
@@ -828,37 +806,32 @@
                 for code_id, tax_id in base_amounts:
                     if not base_amounts[code_id, tax_id]:
                         continue
-                    value = tax_line_obj.default_get(
-                        tax_line_obj._columns.keys())
+                    value = TaxLine.default_get(TaxLine._fields.keys())
                     value.update({
                             'amount': base_amounts[code_id, tax_id],
-                            'currency_digits': account.currency_digits,
+                            'currency_digits': self.account.currency_digits,
                             'code': code_id,
-                            'code.rec_name': tax_code_obj.browse(
-                                code_id).rec_name,
+                            'code.rec_name': TaxCode(code_id).rec_name,
                             'tax': tax_id,
-                            'tax.rec_name': tax_obj.browse(tax_id).rec_name,
+                            'tax.rec_name': Tax(tax_id).rec_name,
                             })
                     res.setdefault('add', []).append(value)
         return res
 
-    def on_change_party(self, vals):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        journal_obj = pool.get('account.journal')
-        currency_obj = pool.get('currency.currency')
+    def on_change_party(self):
+        Journal = Pool().get('account.journal')
         cursor = Transaction().cursor
         res = {}
-        if (not vals.get('party')) or vals.get('account'):
-            return res
-        party = party_obj.browse(vals.get('party'))
-
-        if not party.account_receivable \
-                or not party.account_payable:
+        if (not self.party) or self.account:
             return res
 
-        if party and (not vals.get('debit')) and (not vals.get('credit')):
-            type_name = FIELDS[self.debit._type].sql_type(self.debit)[0]
+        if not self.party.account_receivable \
+                or not self.party.account_payable:
+            return res
+
+        if self.party and (not self.debit) and (not self.credit):
+            type_name = FIELDS[self.__class__.debit._type].sql_type(
+                self.__class__.debit)[0]
             query = 'SELECT ' \
                         'CAST(COALESCE(SUM(' \
                             '(COALESCE(debit, 0) - COALESCE(credit, 0))' \
@@ -867,122 +840,126 @@
                     'WHERE reconciliation IS NULL ' \
                         'AND party = %s ' \
                         'AND account = %s'
-            cursor.execute(query, (party.id, party.account_receivable.id))
+            cursor.execute(query,
+                (self.party.id, self.party.account_receivable.id))
             amount = cursor.fetchone()[0]
             # SQLite uses float for SUM
             if not isinstance(amount, Decimal):
                 amount = Decimal(str(amount))
-            if not currency_obj.is_zero(party.account_receivable.currency,
-                    amount):
+            if not self.party.account_receivable.currency.is_zero(amount):
                 if amount > Decimal('0.0'):
-                    res['credit'] = currency_obj.round(
-                            party.account_receivable.currency, amount)
+                    res['credit'] = \
+                        self.party.account_receivable.currency.round(amount)
                     res['debit'] = Decimal('0.0')
                 else:
                     res['credit'] = Decimal('0.0')
-                    res['debit'] = - currency_obj.round(
-                            party.account_receivable.currency, amount)
-                res['account'] = party.account_receivable.id
-                res['account.rec_name'] = party.account_receivable.rec_name
+                    res['debit'] = \
+                        - self.party.account_receivable.currency.round(amount)
+                res['account'] = self.party.account_receivable.id
+                res['account.rec_name'] = \
+                    self.party.account_receivable.rec_name
             else:
-                cursor.execute(query, (party.id, party.account_payable.id))
+                cursor.execute(query,
+                    (self.party.id, self.party.account_payable.id))
                 amount = cursor.fetchone()[0]
-                if not currency_obj.is_zero(party.account_payable.currency,
-                        amount):
+                if not self.party.account_payable.currency.is_zero(amount):
                     if amount > Decimal('0.0'):
-                        res['credit'] = currency_obj.round(
-                                party.account_payable.currency, amount)
+                        res['credit'] = \
+                            self.party.account_payable.currency.round(amount)
                         res['debit'] = Decimal('0.0')
                     else:
                         res['credit'] = Decimal('0.0')
-                        res['debit'] = - currency_obj.round(
-                                party.account_payable.currency, amount)
-                    res['account'] = party.account_payable.id
-                    res['account.rec_name'] = party.account_payable.rec_name
+                        res['debit'] = \
+                            - self.party.account_payable.currency.round(amount)
+                    res['account'] = self.party.account_payable.id
+                    res['account.rec_name'] = \
+                        self.party.account_payable.rec_name
 
-        if party and vals.get('debit'):
-            if vals['debit'] > Decimal('0.0'):
+        if self.party and self.debit:
+            if self.debit > Decimal('0.0'):
                 if 'account' not in res:
-                    res['account'] = party.account_receivable.id
-                    res['account.rec_name'] = party.account_receivable.rec_name
+                    res['account'] = self.party.account_receivable.id
+                    res['account.rec_name'] = \
+                        self.party.account_receivable.rec_name
             else:
                 if 'account' not in res:
-                    res['account'] = party.account_payable.id
-                    res['account.rec_name'] = party.account_payable.rec_name
+                    res['account'] = self.party.account_payable.id
+                    res['account.rec_name'] = \
+                        self.party.account_payable.rec_name
 
-        if party and vals.get('credit'):
-            if vals['credit'] > Decimal('0.0'):
+        if self.party and self.credit:
+            if self.credit > Decimal('0.0'):
                 if 'account' not in res:
-                    res['account'] = party.account_payable.id
-                    res['account.rec_name'] = party.account_payable.rec_name
+                    res['account'] = self.party.account_payable.id
+                    res['account.rec_name'] = \
+                        self.party.account_payable.rec_name
             else:
                 if 'account' not in res:
-                    res['account'] = party.account_receivable.id
-                    res['account.rec_name'] = party.account_receivable.rec_name
+                    res['account'] = self.party.account_receivable.id
+                    res['account.rec_name'] = \
+                        self.party.account_receivable.rec_name
 
-        journal_id = (vals.get('journal')
-                or Transaction().context.get('journal'))
-        if journal_id and party:
-            journal = journal_obj.browse(journal_id)
+        journal = (self.journal
+                or Journal(Transaction().context.get('journal')))
+        if journal and self.party:
             if journal.type == 'revenue':
                 if 'account' not in res:
-                    res['account'] = party.account_receivable.id
-                    res['account.rec_name'] = party.account_receivable.rec_name
+                    res['account'] = self.party.account_receivable.id
+                    res['account.rec_name'] = \
+                        self.party.account_receivable.rec_name
             elif journal.type == 'expense':
                 if 'account' not in res:
-                    res['account'] = party.account_payable.id
-                    res['account.rec_name'] = party.account_payable.rec_name
+                    res['account'] = self.party.account_payable.id
+                    res['account.rec_name'] = \
+                        self.party.account_payable.rec_name
         return res
 
-    def get_move_field(self, ids, name):
+    def get_move_field(self, name):
         if name == 'move_state':
             name = 'state'
         if name not in ('period', 'journal', 'date', 'state'):
             raise Exception('Invalid name')
-        res = {}
-        for line in self.browse(ids):
-            if name in ('date', 'state'):
-                res[line.id] = line.move[name]
-            else:
-                res[line.id] = line.move[name].id
-        return res
+        if name in ('date', 'state'):
+            return getattr(self.move, name)
+        else:
+            return getattr(self.move, name).id
 
-    def set_move_field(self, ids, name, value):
+    @classmethod
+    def set_move_field(cls, lines, name, value):
         if name == 'move_state':
             name = 'state'
         if name not in ('period', 'journal', 'date', 'state'):
             raise Exception('Invalid name')
         if not value:
             return
-        move_obj = Pool().get('account.move')
-        lines = self.browse(ids)
-        move_obj.write([line.move.id for line in lines], {
-            name: value,
-            })
+        Move = Pool().get('account.move')
+        Move.write([line.move for line in lines], {
+                name: value,
+                })
 
-    def search_move_field(self, name, clause):
+    @classmethod
+    def search_move_field(cls, name, clause):
         if name == 'move_state':
             name = 'state'
         return [('move.' + name,) + tuple(clause[1:])]
 
-    def query_get(self, obj='l'):
+    @classmethod
+    def query_get(cls, obj='l'):
         '''
         Return SQL clause and fiscal years for account move line
         depending of the context.
-
-        :param obj: the SQL alias of account_move_line in the query
-        :return: a tuple with the SQL clause and the fiscalyear ids
+        obj is the SQL alias of account_move_line in the query
         '''
-        fiscalyear_obj = Pool().get('account.fiscalyear')
+        FiscalYear = Pool().get('account.fiscalyear')
 
         if Transaction().context.get('date'):
             time.strptime(str(Transaction().context['date']), '%Y-%m-%d')
-            fiscalyear_ids = fiscalyear_obj.search([
-                ('start_date', '<=', Transaction().context['date']),
-                ('end_date', '>=', Transaction().context['date']),
-                ], limit=1)
+            fiscalyears = FiscalYear.search([
+                    ('start_date', '<=', Transaction().context['date']),
+                    ('end_date', '>=', Transaction().context['date']),
+                    ], limit=1)
 
-            fiscalyear_id = fiscalyear_ids and fiscalyear_ids[0] or 0
+            fiscalyear_id = fiscalyears and fiscalyears[0].id or 0
 
             if Transaction().context.get('posted'):
                 return (obj + '.active ' \
@@ -997,7 +974,7 @@
                                         str(Transaction().context['date']) + \
                                         '\') ' \
                                     'AND m.state = \'posted\' ' \
-                            ')', fiscalyear_ids)
+                            ')', [f.id for f in fiscalyears])
             else:
                 return (obj + '.active ' \
                         'AND ' + obj + '.state != \'draft\' ' \
@@ -1010,7 +987,7 @@
                                     'AND m.date <= date(\'' + \
                                         str(Transaction().context['date']) + \
                                         '\')' \
-                            ')', fiscalyear_ids)
+                            ')', [f.id for f in fiscalyears])
 
         if Transaction().context.get('periods'):
             if Transaction().context.get('fiscalyear'):
@@ -1036,10 +1013,12 @@
                         ')', fiscalyear_ids)
         else:
             if not Transaction().context.get('fiscalyear'):
-                fiscalyear_ids = fiscalyear_obj.search([
+                fiscalyears = FiscalYear.search([
                     ('state', '=', 'open'),
                     ])
-                fiscalyear_clause = (','.join(map(str, fiscalyear_ids))) or '0'
+                fiscalyear_ids = [f.id for f in fiscalyears]
+                fiscalyear_clause = (','.join(
+                        str(f.id) for f in fiscalyears)) or '0'
             else:
                 fiscalyear_ids = [int(Transaction().context.get('fiscalyear'))]
                 fiscalyear_clause = '%s' % int(
@@ -1067,159 +1046,146 @@
                             ')'
                         ')', fiscalyear_ids)
 
-    def on_write(self, ids):
-        lines = self.browse(ids)
-        res = []
-        for line in lines:
-            res.extend([x.id for x in line.move.lines])
-        return list({}.fromkeys(res))
+    @classmethod
+    def on_write(cls, lines):
+        return list(set(l.id for line in lines for l in line.move.lines))
 
-    def check_account(self, ids):
-        for line in self.browse(ids):
-            if line.account.kind in ('view',):
-                return False
-            if not line.account.active:
-                return False
+    def check_account(self):
+        if self.account.kind in ('view',):
+            return False
+        if not self.account.active:
+            return False
         return True
 
-    def check_journal_period_modify(self, period_id, journal_id):
+    @classmethod
+    def check_journal_period_modify(cls, period, journal):
         '''
         Check if the lines can be modified or created for the journal - period
         and if there is no journal - period, create it
         '''
-        pool = Pool()
-        journal_period_obj = pool.get('account.journal.period')
-        journal_obj = pool.get('account.journal')
-        period_obj = pool.get('account.period')
-        journal_period_ids = journal_period_obj.search([
-            ('journal', '=', journal_id),
-            ('period', '=', period_id),
-            ], limit=1)
-        if journal_period_ids:
-            journal_period = journal_period_obj.browse(journal_period_ids[0])
+        JournalPeriod = Pool().get('account.journal.period')
+        journal_periods = JournalPeriod.search([
+                ('journal', '=', journal.id),
+                ('period', '=', period.id),
+                ], limit=1)
+        if journal_periods:
+            journal_period, = journal_periods
             if journal_period.state == 'close':
-                self.raise_user_error('add_modify_closed_journal_period')
+                cls.raise_user_error('add_modify_closed_journal_period')
         else:
-            journal = journal_obj.browse(journal_id)
-            period = period_obj.browse(period_id)
-            journal_period_obj.create({
-                'name': journal.name + ' - ' + period.name,
-                'journal': journal.id,
-                'period': period.id,
-                })
-        return
+            JournalPeriod.create({
+                    'name': journal.name + ' - ' + period.name,
+                    'journal': journal.id,
+                    'period': period.id,
+                    })
 
-    def check_modify(self, ids):
+    @classmethod
+    def check_modify(cls, lines):
         '''
         Check if the lines can be modified
         '''
         journal_period_done = []
-        for line in self.browse(ids):
+        for line in lines:
             if line.move.state == 'posted':
-                self.raise_user_error('modify_posted_move')
+                cls.raise_user_error('modify_posted_move')
             if line.reconciliation:
-                self.raise_user_error('modify_reconciled')
+                cls.raise_user_error('modify_reconciled')
             journal_period = (line.journal.id, line.period.id)
             if journal_period not in journal_period_done:
-                self.check_journal_period_modify(line.period.id,
-                        line.journal.id)
+                cls.check_journal_period_modify(line.period,
+                        line.journal)
                 journal_period_done.append(journal_period)
-        return
 
-    def delete(self, ids):
-        move_obj = Pool().get('account.move')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        self.check_modify(ids)
-        lines = self.browse(ids)
-        move_ids = [x.move.id for x in lines]
-        res = super(Line, self).delete(ids)
-        move_obj.validate(move_ids)
-        return res
+    @classmethod
+    def delete(cls, lines):
+        Move = Pool().get('account.move')
+        cls.check_modify(lines)
+        moves = [x.move for x in lines]
+        super(Line, cls).delete(lines)
+        Move.validate(moves)
 
-    def write(self, ids, vals):
-        move_obj = Pool().get('account.move')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def write(cls, lines, vals):
+        Move = Pool().get('account.move')
 
         if len(vals) > 1 or 'reconciliation' not in vals:
-            self.check_modify(ids)
-        lines = self.browse(ids)
-        move_ids = [x.move.id for x in lines]
-        res = super(Line, self).write(ids, vals)
+            cls.check_modify(lines)
+        moves = [x.move for x in lines]
+        super(Line, cls).write(lines, vals)
 
         Transaction().timestamp = {}
 
-        lines = self.browse(ids)
         for line in lines:
-            if line.move.id not in move_ids:
-                move_ids.append(line.move.id)
-        move_obj.validate(move_ids)
-        return res
+            if line.move not in moves:
+                moves.append(line.move)
+        Move.validate(moves)
 
-    def create(self, vals):
-        journal_obj = Pool().get('account.journal')
-        move_obj = Pool().get('account.move')
+    @classmethod
+    def create(cls, vals):
+        pool = Pool()
+        Journal = pool.get('account.journal')
+        Move = pool.get('account.move')
         vals = vals.copy()
         if not vals.get('move'):
             journal_id = (vals.get('journal')
                     or Transaction().context.get('journal'))
             if not journal_id:
-                self.raise_user_error('no_journal')
-            journal = journal_obj.browse(journal_id)
+                cls.raise_user_error('no_journal')
+            journal = Journal(journal_id)
             if journal.centralised:
-                move_ids = move_obj.search([
-                    ('period', '=', vals.get('period')
-                        or Transaction().context.get('period')),
-                    ('journal', '=', journal_id),
-                    ('state', '!=', 'posted'),
-                    ], limit=1)
-                if move_ids:
-                    vals['move'] = move_ids[0]
+                moves = Move.search([
+                        ('period', '=', vals.get('period')
+                            or Transaction().context.get('period')),
+                        ('journal', '=', journal_id),
+                        ('state', '!=', 'posted'),
+                        ], limit=1)
+                if moves:
+                    vals['move'] = moves[0].id
             if not vals.get('move'):
-                vals['move'] = move_obj.create({
-                    'period': (vals.get('period')
-                        or Transaction().context.get('period')),
-                    'journal': journal_id,
-                    'date': vals.get('date'),
-                    })
-        res = super(Line, self).create(vals)
-        line = self.browse(res)
-        self.check_journal_period_modify(line.period.id, line.journal.id)
-        move_obj.validate([vals['move']])
-        return res
+                vals['move'] = Move.create({
+                        'period': (vals.get('period')
+                            or Transaction().context.get('period')),
+                        'journal': journal_id,
+                        'date': vals.get('date'),
+                        }).id
+        line = super(Line, cls).create(vals)
+        cls.check_journal_period_modify(line.period, line.journal)
+        Move.validate([line.move])
+        return line
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, lines, default=None):
         if default is None:
             default = {}
         if 'move' not in default:
             default['move'] = None
         if 'reconciliation' not in default:
             default['reconciliation'] = None
-        return super(Line, self).copy(ids, default=default)
+        return super(Line, cls).copy(lines, default=default)
 
-    def view_header_get(self, value, view_type='form'):
-        journal_period_obj = Pool().get('account.journal.period')
+    @classmethod
+    def view_header_get(cls, value, view_type='form'):
+        JournalPeriod = Pool().get('account.journal.period')
         if (not Transaction().context.get('journal')
                 or not Transaction().context.get('period')):
             return value
-        journal_period_ids = journal_period_obj.search([
-            ('journal', '=', Transaction().context['journal']),
-            ('period', '=', Transaction().context['period']),
-            ], limit=1)
-        if not journal_period_ids:
+        journal_periods = JournalPeriod.search([
+                ('journal', '=', Transaction().context['journal']),
+                ('period', '=', Transaction().context['period']),
+                ], limit=1)
+        if not journal_periods:
             return value
-        journal_period = journal_period_obj.browse(journal_period_ids[0])
+        journal_period, = journal_periods
         return value + ': ' + journal_period.rec_name
 
-    def fields_view_get(self, view_id=None, view_type='form'):
-        journal_obj = Pool().get('account.journal')
-        result = super(Line, self).fields_view_get(view_id=view_id,
+    @classmethod
+    def fields_view_get(cls, view_id=None, view_type='form'):
+        Journal = Pool().get('account.journal')
+        result = super(Line, cls).fields_view_get(view_id=view_id,
             view_type=view_type)
         if view_type == 'tree' and 'journal' in Transaction().context:
-            title = self.view_header_get('', view_type=view_type)
-            journal = journal_obj.browse(Transaction().context['journal'])
+            title = cls.view_header_get('', view_type=view_type)
+            journal = Journal(Transaction().context['journal'])
 
             if not journal.view:
                 return result
@@ -1243,48 +1209,48 @@
                     attrs.append('required="0"')
                 xml += ('<field name="%s" %s/>\n'
                     % (column.field.name, ' '.join(attrs)))
-                for depend in getattr(self, column.field.name).depends:
+                for depend in getattr(cls, column.field.name).depends:
                     fields.add(depend)
             fields.add('state')
             xml += '</tree>'
             result['arch'] = xml
-            result['fields'] = self.fields_get(fields_names=list(fields))
+            result['fields'] = cls.fields_get(fields_names=list(fields))
         return result
 
-    def reconcile(self, ids, journal_id=None, date=None, account_id=None):
+    @classmethod
+    def reconcile(cls, lines, journal=None, date=None, account=None):
         pool = Pool()
-        move_obj = pool.get('account.move')
-        currency_obj = pool.get('currency.currency')
-        reconciliation_obj = pool.get('account.move.reconciliation')
-        period_obj = pool.get('account.period')
-        date_obj = pool.get('ir.date')
-        translation_obj = pool.get('ir.translation')
+        Move = pool.get('account.move')
+        Reconciliation = pool.get('account.move.reconciliation')
+        Period = pool.get('account.period')
+        Date = pool.get('ir.date')
+        Translation = pool.get('ir.translation')
 
-        for line in self.browse(ids):
+        for line in lines:
             if line.reconciliation:
-                self.raise_user_error('already_reconciled',
+                cls.raise_user_error('already_reconciled',
                         error_args=(line.name, line.id,))
 
-        ids = ids[:]
-        if journal_id and account_id:
+        lines = lines[:]
+        if journal and account:
             if not date:
-                date = date_obj.today()
+                date = Date.today()
             account = None
             amount = Decimal('0.0')
-            for line in self.browse(ids):
+            for line in lines:
                 amount += line.debit - line.credit
                 if not account:
                     account = line.account
-            amount = currency_obj.round(account.currency, amount)
-            period_id = period_obj.find(account.company.id, date=date)
+            amount = account.currency.round(amount)
+            period_id = Period.find(account.company.id, date=date)
             lang_code = CONFIG['language']
             if account.company.lang:
                 lang_code = account.company.lang.code
-            writeoff = translation_obj.get_source(
+            writeoff = Translation.get_source(
                     'account.move.reconcile_lines.writeoff', 'view', lang_code,
                     'Write-Off') or 'Write-Off'
-            move_id = move_obj.create({
-                'journal': journal_id,
+            move = Move.create({
+                'journal': journal.id,
                 'period': period_id,
                 'date': date,
                 'lines': [
@@ -1298,7 +1264,7 @@
                     }),
                     ('create', {
                         'name': writeoff,
-                        'account': account_id,
+                        'account': account.id,
                         'debit': amount > Decimal('0.0') and amount \
                                 or Decimal('0.0'),
                         'credit': amount < Decimal('0.0') and - amount \
@@ -1306,33 +1272,28 @@
                     }),
                 ],
                 })
-            ids += self.search([
-                ('move', '=', move_id),
+            lines += cls.search([
+                ('move', '=', move.id),
                 ('account', '=', account.id),
                 ('debit', '=', amount < Decimal('0.0') and - amount \
                         or Decimal('0.0')),
                 ('credit', '=', amount > Decimal('0.0') and amount \
                         or Decimal('0.0')),
                 ], limit=1)
-        return reconciliation_obj.create({
-            'lines': [('add', x) for x in ids],
-            })
+        return Reconciliation.create({
+                'lines': [('add', [x.id for x in lines])],
+                })
 
-Line()
 
-
-class Move2(ModelSQL, ModelView):
-    _name = 'account.move'
+class Move2:
+    __name__ = 'account.move'
     centralised_line = fields.Many2One('account.move.line', 'Centralised Line',
             readonly=True)
 
-Move2()
-
 
 class OpenJournalAsk(ModelView):
     'Open Journal Ask'
-    _name = 'account.move.open_journal.ask'
-    _description = __doc__
+    __name__ = 'account.move.open_journal.ask'
     journal = fields.Many2One('account.journal', 'Journal', required=True)
     period = fields.Many2One('account.period', 'Period', required=True,
         domain=[
@@ -1341,18 +1302,16 @@
                 Eval('context', {}).get('company', 0)),
             ])
 
-    def default_period(self):
-        period_obj = Pool().get('account.period')
-        return period_obj.find(Transaction().context.get('company'),
+    @staticmethod
+    def default_period():
+        Period = Pool().get('account.period')
+        return Period.find(Transaction().context.get('company'),
                 exception=False)
 
-OpenJournalAsk()
-
 
 class OpenJournal(Wizard):
     'Open Journal'
-    _name = 'account.move.open_journal'
-
+    __name__ = 'account.move.open_journal'
     start = StateTransition()
     ask = StateView('account.move.open_journal.ask',
         'account.open_journal_ask_view_form', [
@@ -1361,99 +1320,85 @@
             ])
     open_ = StateAction('account.act_move_line_form')
 
-    def transition_start(self, session):
+    def transition_start(self):
         if (Transaction().context.get('active_model', '')
                 == 'account.journal.period'
                 and Transaction().context.get('active_id')):
             return 'open_'
         return 'ask'
 
-    def default_ask(self, session, fields):
-        journal_period_obj = Pool().get('account.journal.period')
+    def default_ask(self, fields):
+        JournalPeriod = Pool().get('account.journal.period')
         if (Transaction().context.get('active_model', '') ==
                 'account.journal.period'
                 and Transaction().context.get('active_id')):
-            journal_period = \
-                journal_period_obj.browse(Transaction().context['active_id'])
+            journal_period = JournalPeriod(Transaction().context['active_id'])
             return {
                 'journal': journal_period.journal.id,
                 'period': journal_period.period.id,
-            }
+                }
         return {}
 
-    def do_open_(self, session, action):
-        pool = Pool()
-        journal_period_obj = pool.get('account.journal.period')
-        journal_obj = pool.get('account.journal')
-        period_obj = pool.get('account.period')
+    def do_open_(self, action):
+        JournalPeriod = Pool().get('account.journal.period')
 
         if (Transaction().context.get('active_model', '') ==
                 'account.journal.period'
                 and Transaction().context.get('active_id')):
-            journal_period = \
-                journal_period_obj.browse(Transaction().context['active_id'])
-            journal_id = journal_period.journal.id
-            period_id = journal_period.period.id
+            journal_period = JournalPeriod(Transaction().context['active_id'])
+            journal = journal_period.journal
+            period = journal_period.period
         else:
-            journal_id = session.ask.journal.id
-            period_id = session.ask.period.id
-        journal_period_ids = journal_period_obj.search([
-            ('journal', '=', journal_id),
-            ('period', '=', period_id),
-            ])
-        if not journal_period_ids:
-            journal = journal_obj.browse(journal_id)
-            period = period_obj.browse(period_id)
-            journal_period_id = journal_period_obj.create({
+            journal = self.ask.journal
+            period = self.ask.period
+        journal_periods = JournalPeriod.search([
+                ('journal', '=', journal.id),
+                ('period', '=', period.id),
+                ], limit=1)
+        if not journal_periods:
+            journal_period = JournalPeriod.create({
                     'name': journal.name + ' - ' + period.name,
                     'journal': journal.id,
                     'period': period.id,
                     })
         else:
-            journal_period_id = journal_period_ids[0]
-        journal_period = journal_period_obj.browse(journal_period_id)
+            journal_period, = journal_periods
 
         action['name'] += ' - %s' % journal_period.rec_name
         action['pyson_domain'] = PYSONEncoder().encode([
-            ('journal', '=', journal_id),
-            ('period', '=', period_id),
+            ('journal', '=', journal.id),
+            ('period', '=', period.id),
             ])
         action['pyson_context'] = PYSONEncoder().encode({
-            'journal': journal_id,
-            'period': period_id,
+            'journal': journal.id,
+            'period': period.id,
             })
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
 
-OpenJournal()
-
 
 class OpenAccount(Wizard):
     'Open Account'
-    _name = 'account.move.open_account'
+    __name__ = 'account.move.open_account'
     start_state = 'open_'
     open_ = StateAction('account.act_move_line_form')
 
-    def do_open_(self, session, action):
-        pool = Pool()
-        fiscalyear_obj = pool.get('account.fiscalyear')
+    def do_open_(self, action):
+        FiscalYear = Pool().get('account.fiscalyear')
 
         if not Transaction().context.get('fiscalyear'):
-            fiscalyear_ids = fiscalyear_obj.search([
-                ('state', '=', 'open'),
-                ])
+            fiscalyears = FiscalYear.search([
+                    ('state', '=', 'open'),
+                    ])
         else:
-            fiscalyear_ids = [Transaction().context['fiscalyear']]
+            fiscalyears = [FiscalYear(Transaction().context['fiscalyear'])]
 
-        period_ids = []
-        for fiscalyear in fiscalyear_obj.browse(fiscalyear_ids):
-            for period in fiscalyear.periods:
-                period_ids.append(period.id)
+        periods = [p for f in fiscalyears for p in f.periods]
 
         action['pyson_domain'] = [
-            ('period', 'in', period_ids),
+            ('period', 'in', [p.id for p in periods]),
             ('account', '=', Transaction().context['active_id']),
             ]
         if Transaction().context.get('posted'):
@@ -1467,29 +1412,24 @@
         })
         return action, {}
 
-OpenAccount()
-
 
 class ReconcileLinesWriteOff(ModelView):
     'Reconcile Lines Write-Off'
-    _name = 'account.move.reconcile_lines.writeoff'
-    _description = __doc__
+    __name__ = 'account.move.reconcile_lines.writeoff'
     journal = fields.Many2One('account.journal', 'Journal', required=True)
     date = fields.Date('Date', required=True)
     account = fields.Many2One('account.account', 'Account', required=True,
             domain=[('kind', '!=', 'view')])
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
-
-ReconcileLinesWriteOff()
+    @staticmethod
+    def default_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
 
 class ReconcileLines(Wizard):
     'Reconcile Lines'
-    _name = 'account.move.reconcile_lines'
-
+    __name__ = 'account.move.reconcile_lines'
     start = StateTransition()
     writeoff = StateView('account.move.reconcile_lines.writeoff',
         'account.reconcile_lines_writeoff_view_form', [
@@ -1498,84 +1438,71 @@
             ])
     reconcile = StateTransition()
 
-    def transition_start(self, session):
-        line_obj = Pool().get('account.move.line')
-        currency_obj = Pool().get('currency.currency')
+    def transition_start(self):
+        Line = Pool().get('account.move.line')
 
         company = None
         amount = Decimal('0.0')
-        for line in line_obj.browse(Transaction().context['active_ids']):
+        for line in Line.browse(Transaction().context['active_ids']):
             amount += line.debit - line.credit
             if not company:
                 company = line.account.company
         if not company:
             return 'end'
-        if currency_obj.is_zero(company.currency, amount):
+        if company.currency.is_zero(amount):
             return 'reconcile'
         return 'writeoff'
 
-    def transition_reconcile(self, session):
-        line_obj = Pool().get('account.move.line')
+    def transition_reconcile(self):
+        Line = Pool().get('account.move.line')
 
-        journal_id = None
+        journal = None
         date = None
-        account_id = None
-        if session.writeoff.journal:
-            journal_id = session.writeoff.journal.id
-        if session.writeoff.date:
-            date = session.writeoff.date
-        if session.writeoff.account:
-            account_id = session.writeoff.account.id
-        line_obj.reconcile(Transaction().context['active_ids'], journal_id,
-            date, account_id)
+        account = None
+        if self.writeoff.journal:
+            journal = self.writeoff.journal
+        if self.writeoff.date:
+            date = self.writeoff.date
+        if self.writeoff.account:
+            account = self.writeoff.account
+        Line.reconcile(Line.browse(Transaction().context['active_ids']),
+            journal, date, account)
         return 'end'
 
-ReconcileLines()
-
 
 class UnreconcileLinesStart(ModelView):
     'Unreconcile Lines'
-    _name = 'account.move.unreconcile_lines.start'
-    _description = __doc__
-
-UnreconcileLinesStart()
+    __name__ = 'account.move.unreconcile_lines.start'
 
 
 class UnreconcileLines(Wizard):
     'Unreconcile Lines'
-    _name = 'account.move.unreconcile_lines'
+    __name__ = 'account.move.unreconcile_lines'
     start_state = 'unreconcile'
-
     unreconcile = StateTransition()
 
-    def transition_unreconcile(self, session):
-        line_obj = Pool().get('account.move.line')
-        reconciliation_obj = Pool().get('account.move.reconciliation')
+    def transition_unreconcile(self):
+        pool = Pool()
+        Line = pool.get('account.move.line')
+        Reconciliation = pool.get('account.move.reconciliation')
 
-        lines = line_obj.browse(Transaction().context['active_ids'])
-        reconciliation_ids = [x.reconciliation.id for x in lines \
-                if x.reconciliation]
-        if reconciliation_ids:
-            reconciliation_obj.delete(reconciliation_ids)
+        lines = Line.browse(Transaction().context['active_ids'])
+        reconciliations = [x.reconciliation for x in lines if x.reconciliation]
+        if reconciliations:
+            Reconciliation.delete(reconciliations)
         return 'end'
 
-UnreconcileLines()
-
 
 class OpenReconcileLinesStart(ModelView):
     'Open Reconcile Lines'
-    _name = 'account.move.open_reconcile_lines.start'
-    _description = __doc__
+    __name__ = 'account.move.open_reconcile_lines.start'
     account = fields.Many2One('account.account', 'Account', required=True,
             domain=[('kind', '!=', 'view'), ('reconcile', '=', True)])
 
-OpenReconcileLinesStart()
-
 
 class OpenReconcileLines(Wizard):
     'Open Reconcile Lines'
-    _name = 'account.move.open_reconcile_lines'
-
+    __name__ = 'account.move.open_reconcile_lines'
     start = StateView('account.move.open_reconcile_lines.start',
         'account.open_reconcile_lines_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1583,67 +1510,60 @@
             ])
     open_ = StateAction('account.act_move_line_form')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         action['pyson_domain'] = PYSONEncoder().encode([
-            ('account', '=', session.start.account.id),
+            ('account', '=', self.start.account.id),
             ('reconciliation', '=', None),
             ])
         return action, {}
 
-OpenReconcileLines()
-
 
 class FiscalYearLine(ModelSQL):
     'Fiscal Year - Move Line'
-    _name = 'account.fiscalyear-account.move.line'
+    __name__ = 'account.fiscalyear-account.move.line'
     _table = 'account_fiscalyear_line_rel'
-    _description = __doc__
     fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
             ondelete='CASCADE', select=True)
     line = fields.Many2One('account.move.line', 'Line', ondelete='RESTRICT',
             select=True, required=True)
 
-FiscalYearLine()
 
-
-class FiscalYear(ModelSQL, ModelView):
-    _name = 'account.fiscalyear'
+class FiscalYear2:
+    __name__ = 'account.fiscalyear'
     close_lines = fields.Many2Many('account.fiscalyear-account.move.line',
             'fiscalyear', 'line', 'Close Lines')
 
-FiscalYear()
-
 
 class PrintGeneralJournalStart(ModelView):
     'Print General Journal'
-    _name = 'account.move.print_general_journal.start'
-    _description = __doc__
+    __name__ = 'account.move.print_general_journal.start'
     from_date = fields.Date('From Date', required=True)
     to_date = fields.Date('To Date', required=True)
     company = fields.Many2One('company.company', 'Company', required=True)
     posted = fields.Boolean('Posted Move', help='Show only posted move')
 
-    def default_from_date(self):
-        date_obj = Pool().get('ir.date')
-        return datetime.date(date_obj.today().year, 1, 1)
+    @staticmethod
+    def default_from_date():
+        Date = Pool().get('ir.date')
+        return datetime.date(Date.today().year, 1, 1)
 
-    def default_to_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_to_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_posted(self):
+    @staticmethod
+    def default_posted():
         return False
 
-PrintGeneralJournalStart()
-
 
 class PrintGeneralJournal(Wizard):
     'Print General Journal'
-    _name = 'account.move.print_general_journal'
-
+    __name__ = 'account.move.print_general_journal'
     start = StateView('account.move.print_general_journal.start',
         'account.print_general_journal_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -1651,23 +1571,22 @@
             ])
     print_ = StateAction('account.report_general_journal')
 
-    def do_print_(self, session, action):
+    def do_print_(self, action):
         data = {
-            'company': session.start.company.id,
-            'from_date': session.start.from_date,
-            'to_date': session.start.to_date,
-            'posted': session.start.posted,
+            'company': self.start.company.id,
+            'from_date': self.start.from_date,
+            'to_date': self.start.to_date,
+            'posted': self.start.posted,
             }
         return action, data
 
-PrintGeneralJournal()
-
 
 class GeneralJournal(Report):
-    _name = 'account.move.general_journal'
+    __name__ = 'account.move.general_journal'
 
-    def _get_objects(self, ids, model, data):
-        move_obj = Pool().get('account.move')
+    @classmethod
+    def _get_records(cls, ids, model, data):
+        Move = Pool().get('account.move')
 
         clause = [
             ('date', '>=', data['from_date']),
@@ -1675,21 +1594,19 @@
             ]
         if data['posted']:
             clause.append(('state', '=', 'posted'))
-        move_ids = move_obj.search(clause,
+        return Move.search(clause,
                 order=[('date', 'ASC'), ('reference', 'ASC'), ('id', 'ASC')])
-        return move_obj.browse(move_ids)
 
-    def parse(self, report, objects, data, localcontext):
-        company_obj = Pool().get('company.company')
+    @classmethod
+    def parse(cls, report, moves, data, localcontext):
+        Company = Pool().get('company.company')
 
-        company = company_obj.browse(data['company'])
+        company = Company(data['company'])
 
         localcontext['company'] = company
         localcontext['digits'] = company.currency.digits
         localcontext['from_date'] = data['from_date']
         localcontext['to_date'] = data['to_date']
 
-        return super(GeneralJournal, self).parse(report, objects, data,
+        return super(GeneralJournal, cls).parse(report, moves, data,
             localcontext)
-
-GeneralJournal()
diff -r f40675df5d4c trytond/modules/account/party.py
--- a/trytond/modules/account/party.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/party.py	Mon Sep 24 20:36:38 2012 +0200
@@ -1,14 +1,17 @@
 #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 trytond.model import ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval, Bool
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Party']
+__metaclass__ = PoolMeta
 
-class Party(ModelSQL, ModelView):
-    _name = 'party.party'
+
+class Party:
+    __name__ = 'party.party'
     account_payable = fields.Property(fields.Many2One('account.account',
             'Account Payable', domain=[
                 ('kind', '=', 'payable'),
@@ -48,40 +51,33 @@
     payable_today = fields.Function(fields.Numeric('Payable Today'),
             'get_receivable_payable', searcher='search_receivable_payable')
 
-    def get_receivable_payable(self, ids, names):
+    @classmethod
+    def get_receivable_payable(cls, parties, names):
         '''
         Function to compute receivable, payable (today or not) for party ids.
-
-        :param ids: the ids of the party
-        :param names: the list of field name to compute
-        :return: a dictionary with all field names as key and
-            a dictionary as value with id as key
         '''
         res = {}
         pool = Pool()
-        move_line_obj = pool.get('account.move.line')
-        user_obj = pool.get('res.user')
-        date_obj = pool.get('ir.date')
+        MoveLine = pool.get('account.move.line')
+        User = pool.get('res.user')
+        Date = pool.get('ir.date')
         cursor = Transaction().cursor
 
         for name in names:
             if name not in ('receivable', 'payable',
                     'receivable_today', 'payable_today'):
                 raise Exception('Bad argument')
-            res[name] = dict((x, Decimal('0.0')) for x in ids)
-
-        if not ids:
-            return {}
+            res[name] = dict((p.id, Decimal('0.0')) for p in parties)
 
         user_id = Transaction().user
         if user_id == 0 and 'user' in Transaction().context:
             user_id = Transaction().context['user']
-        user = user_obj.browse(user_id)
+        user = User(user_id)
         company_id = user.company.id
         if not company_id:
             return res
 
-        line_query, _ = move_line_obj.query_get()
+        line_query, _ = MoveLine.query_get()
 
         for name in names:
             code = name
@@ -91,7 +87,7 @@
                 code = name[:-6]
                 today_query = 'AND (l.maturity_date <= %s ' \
                         'OR l.maturity_date IS NULL) '
-                today_value = [date_obj.today()]
+                today_value = [Date.today()]
 
             cursor.execute('SELECT l.party, '
                     'SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))) '
@@ -100,13 +96,13 @@
                     'AND a.active '
                     'AND a.kind = %s '
                     'AND l.party IN '
-                        '(' + ','.join(('%s',) * len(ids)) + ') '
+                        '(' + ','.join(('%s',) * len(parties)) + ') '
                     'AND l.reconciliation IS NULL '
                     'AND ' + line_query + ' '
                     + today_query +
                     'AND a.company = %s '
                 'GROUP BY l.party',
-                [code] + ids + today_value + [company_id])
+                [code] + [p.id for p in parties] + today_value + [company_id])
             for party_id, sum in cursor.fetchall():
                 # SQLite uses float for SUM
                 if not isinstance(sum, Decimal):
@@ -114,12 +110,13 @@
                 res[name][party_id] = sum
         return res
 
-    def search_receivable_payable(self, name, clause):
+    @classmethod
+    def search_receivable_payable(cls, name, clause):
         pool = Pool()
-        move_line_obj = pool.get('account.move.line')
-        company_obj = pool.get('company.company')
-        user_obj = pool.get('res.user')
-        date_obj = pool.get('ir.date')
+        MoveLine = pool.get('account.move.line')
+        Company = pool.get('company.company')
+        User = pool.get('res.user')
+        Date = pool.get('ir.date')
         cursor = Transaction().cursor
 
         if name not in ('receivable', 'payable',
@@ -130,12 +127,12 @@
         user_id = Transaction().user
         if user_id == 0 and 'user' in Transaction().context:
             user_id = Transaction().context['user']
-        user = user_obj.browse(user_id)
+        user = User(user_id)
         if Transaction().context.get('company'):
-            child_company_ids = company_obj.search([
-                ('parent', 'child_of', [user.main_company.id]),
-                ])
-            if Transaction().context['company'] in child_company_ids:
+            child_companies = Company.search([
+                    ('parent', 'child_of', [user.main_company.id]),
+                    ])
+            if Transaction().context['company'] in child_companies:
                 company_id = Transaction().context['company']
 
         if not company_id:
@@ -151,9 +148,9 @@
             code = name[:-6]
             today_query = 'AND (l.maturity_date <= %s ' \
                     'OR l.maturity_date IS NULL) '
-            today_value = [date_obj.today()]
+            today_value = [Date.today()]
 
-        line_query, _ = move_line_obj.query_get()
+        line_query, _ = MoveLine.query_get()
 
         cursor.execute('SELECT l.party '
             'FROM account_move_line AS l, account_account AS a '
@@ -170,5 +167,3 @@
                 + clause[1] + ' %s)',
             [code] + today_value + [company_id] + [Decimal(clause[2] or 0)])
         return [('id', 'in', [x[0] for x in cursor.fetchall()])]
-
-Party()
diff -r f40675df5d4c trytond/modules/account/period.py
--- a/trytond/modules/account/period.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/period.py	Mon Sep 24 20:36:38 2012 +0200
@@ -1,10 +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.
-from trytond.model import ModelView, ModelSQL, fields, OPERATORS
+from trytond.model import ModelView, ModelSQL, fields
 from trytond.wizard import Wizard, StateTransition
 from trytond.pyson import Eval
 from trytond.transaction import Transaction
 from trytond.pool import Pool
+from trytond.const import OPERATORS
+
+__all__ = ['Period', 'ClosePeriod', 'ReOpenPeriod']
 
 _STATES = {
     'readonly': Eval('state') == 'close',
@@ -14,9 +17,7 @@
 
 class Period(ModelSQL, ModelView):
     'Period'
-    _name = 'account.period'
-    _description = __doc__
-
+    __name__ = 'account.period'
     name = fields.Char('Name', required=True)
     code = fields.Char('Code')
     start_date = fields.Date('Starting Date', required=True, states=_STATES,
@@ -42,15 +43,16 @@
     company = fields.Function(fields.Many2One('company.company', 'Company',),
         'get_company', searcher='search_company')
 
-    def __init__(self):
-        super(Period, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Period, cls).__setup__()
+        cls._constraints += [
             ('check_dates', 'periods_overlaps'),
             ('check_fiscalyear_dates', 'fiscalyear_dates'),
             ('check_post_move_sequence', 'check_move_sequence'),
         ]
-        self._order.insert(0, ('start_date', 'ASC'))
-        self._error_messages.update({
+        cls._order.insert(0, ('start_date', 'ASC'))
+        cls._error_messages.update({
             'no_period_date': 'No period defined for this date!',
             'modify_del_period_moves': 'You can not modify/delete ' \
                     'a period with moves!',
@@ -71,79 +73,68 @@
                     'the fiscal year dates',
             })
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'open'
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'standard'
 
-    def get_company(self, ids, name):
-        result = {}
-        for period in self.browse(ids):
-            result[period.id] = period.fiscalyear.company.id
-        return result
+    def get_company(self, name):
+        return self.fiscalyear.company.id
 
-    def search_company(self, name, clause):
+    @classmethod
+    def search_company(cls, name, clause):
         return [('fiscalyear.%s' % name,) + tuple(clause[1:])]
 
-    def check_dates(self, ids):
+    def check_dates(self):
         cursor = Transaction().cursor
-        for period in self.browse(ids):
-            if period.type != 'standard':
-                continue
-            cursor.execute('SELECT id ' \
-                    'FROM "' + self._table + '" ' \
-                    'WHERE ((start_date <= %s AND end_date >= %s) ' \
-                            'OR (start_date <= %s AND end_date >= %s) ' \
-                            'OR (start_date >= %s AND end_date <= %s)) ' \
-                        'AND fiscalyear = %s ' \
-                        'AND type = \'standard\' ' \
-                        'AND id != %s',
-                    (period.start_date, period.start_date,
-                        period.end_date, period.end_date,
-                        period.start_date, period.end_date,
-                        period.fiscalyear.id, period.id))
-            if cursor.fetchone():
-                return False
+        if self.type != 'standard':
+            return True
+        cursor.execute('SELECT id ' \
+                'FROM "' + self._table + '" ' \
+                'WHERE ((start_date <= %s AND end_date >= %s) ' \
+                        'OR (start_date <= %s AND end_date >= %s) ' \
+                        'OR (start_date >= %s AND end_date <= %s)) ' \
+                    'AND fiscalyear = %s ' \
+                    'AND type = \'standard\' ' \
+                    'AND id != %s',
+                (self.start_date, self.start_date,
+                    self.end_date, self.end_date,
+                    self.start_date, self.end_date,
+                    self.fiscalyear.id, self.id))
+        return not cursor.fetchone()
+
+    def check_fiscalyear_dates(self):
+        return not (self.start_date < self.fiscalyear.start_date
+            or self.end_date > self.fiscalyear.end_date)
+
+    def check_post_move_sequence(self):
+        if self.search([
+                    ('post_move_sequence', '=', self.post_move_sequence.id),
+                    ('fiscalyear', '!=', self.fiscalyear.id),
+                    ]):
+            return False
+        if (self.post_move_sequence.company
+                and (self.post_move_sequence.company.id !=
+                    self.fiscalyear.company.id)):
+            return False
         return True
 
-    def check_fiscalyear_dates(self, ids):
-        for period in self.browse(ids):
-            if period.start_date < period.fiscalyear.start_date \
-                    or period.end_date > period.fiscalyear.end_date:
-                return False
-        return True
-
-    def check_post_move_sequence(self, ids):
-        for period in self.browse(ids):
-            if self.search([
-                ('post_move_sequence', '=', period.post_move_sequence.id),
-                ('fiscalyear', '!=', period.fiscalyear.id),
-                ]):
-                return False
-            if period.post_move_sequence.company \
-                    and period.post_move_sequence.company.id != \
-                    period.fiscalyear.company.id:
-                return False
-        return True
-
-    def find(self, company_id, date=None, exception=True, test_state=True):
+    @classmethod
+    def find(cls, company_id, date=None, exception=True, test_state=True):
         '''
         Return the period for the company_id
             at the date or the current date.
         If exception is set the function will raise an exception
             if no period is found.
-
-        :param company_id: the company id
-        :param date: the date searched
-        :param exception: a boolean to raise or not an exception
-        :param test_state: a boolean if true will search on non-closed periods
-        :return: the period id found or None
+        If test_state is true, it will search on non-closed periods
         '''
-        date_obj = Pool().get('ir.date')
+        Date = Pool().get('ir.date')
 
         if not date:
-            date = date_obj.today()
+            date = Date.today()
         clause = [
             ('start_date', '<=', date),
             ('end_date', '>=', date),
@@ -152,26 +143,25 @@
             ]
         if test_state:
             clause.append(('state', '!=', 'close'))
-        ids = self.search(clause, order=[('start_date', 'DESC')], limit=1)
-        if not ids:
+        periods = cls.search(clause, order=[('start_date', 'DESC')], limit=1)
+        if not periods:
             if exception:
-                self.raise_user_error('no_period_date')
+                cls.raise_user_error('no_period_date')
             else:
                 return None
-        return ids[0]
+        return periods[0].id
 
-    def _check(self, ids):
-        move_obj = Pool().get('account.move')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        move_ids = move_obj.search([
-            ('period', 'in', ids),
-            ], limit=1)
-        if move_ids:
-            self.raise_user_error('modify_del_period_moves')
-        return
+    @classmethod
+    def _check(cls, periods):
+        Move = Pool().get('account.move')
+        moves = Move.search([
+                ('period', 'in', [p.id for p in periods]),
+                ], limit=1)
+        if moves:
+            cls.raise_user_error('modify_del_period_moves')
 
-    def search(self, args, offset=0, limit=None, order=None, count=False,
+    @classmethod
+    def search(cls, args, offset=0, limit=None, order=None, count=False,
             query_string=False):
         args = args[:]
 
@@ -187,107 +177,104 @@
                     if not args[i][2][0]:
                         args[i] = ('id', '!=', '0')
                     else:
-                        period = self.browse(args[i][2][0])
+                        period = cls.browse(args[i][2][0])
                         args[i] = (args[i][0], args[i][1],
                             period[args[i][2][1]])
                 elif isinstance(args[i], list):
                     process_args(args[i])
                 i += 1
         process_args(args)
-        return super(Period, self).search(args, offset=offset, limit=limit,
-                order=order, count=count, query_string=query_string)
+        return super(Period, cls).search(args, offset=offset, limit=limit,
+            order=order, count=count, query_string=query_string)
 
-    def create(self, vals):
-        fiscalyear_obj = Pool().get('account.fiscalyear')
+    @classmethod
+    def create(cls, vals):
+        FiscalYear = Pool().get('account.fiscalyear')
         vals = vals.copy()
         if vals.get('fiscalyear'):
-            fiscalyear = fiscalyear_obj.browse(vals['fiscalyear'])
+            fiscalyear = FiscalYear(vals['fiscalyear'])
             if fiscalyear.state == 'close':
-                self.raise_user_error('create_period_closed_fiscalyear')
+                cls.raise_user_error('create_period_closed_fiscalyear')
             if not vals.get('post_move_sequence'):
                 vals['post_move_sequence'] = fiscalyear.post_move_sequence.id
-        return super(Period, self).create(vals)
+        return super(Period, cls).create(vals)
 
-    def write(self, ids, vals):
-        move_obj = Pool().get('account.move')
+    @classmethod
+    def write(cls, periods, vals):
+        Move = Pool().get('account.move')
         for key in vals.keys():
             if key in ('start_date', 'end_date', 'fiscalyear'):
-                self._check(ids)
+                cls._check(periods)
                 break
         if vals.get('state') == 'open':
-            for period in self.browse(ids):
+            for period in periods:
                 if period.fiscalyear.state == 'close':
-                    self.raise_user_error('open_period_closed_fiscalyear')
+                    cls.raise_user_error('open_period_closed_fiscalyear')
         if vals.get('post_move_sequence'):
-            for period in self.browse(ids):
+            for period in periods:
                 if period.post_move_sequence and \
                         period.post_move_sequence.id != \
                         vals['post_move_sequence']:
-                    if move_obj.search([
-                        ('period', '=', period.id),
-                        ('state', '=', 'posted'),
-                        ]):
-                        self.raise_user_error('change_post_move_sequence')
-        return super(Period, self).write(ids, vals)
+                    if Move.search([
+                                ('period', '=', period.id),
+                                ('state', '=', 'posted'),
+                                ]):
+                        cls.raise_user_error('change_post_move_sequence')
+        super(Period, cls).write(periods, vals)
 
-    def delete(self, ids):
-        self._check(ids)
-        return super(Period, self).delete(ids)
+    @classmethod
+    def delete(cls, periods):
+        cls._check(periods)
+        super(Period, cls).delete(periods)
 
-    def close(self, ids):
-        journal_period_obj = Pool().get('account.journal.period')
-        move_obj = Pool().get('account.move')
+    @classmethod
+    def close(cls, periods):
+        pool = Pool()
+        JournalPeriod = pool.get('account.journal.period')
+        Move = pool.get('account.move')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        if move_obj.search([
-            ('period', 'in', ids),
-            ('state', '!=', 'posted'),
-            ]):
-            self.raise_user_error('close_period_non_posted_move')
+        if Move.search([
+                    ('period', 'in', [p.id for p in periods]),
+                    ('state', '!=', 'posted'),
+                    ]):
+            cls.raise_user_error('close_period_non_posted_move')
         #First close the period to be sure
         #it will not have new journal.period created between.
-        self.write(ids, {
-            'state': 'close',
-            })
-        journal_period_ids = journal_period_obj.search([
-            ('period', 'in', ids),
+        cls.write(periods, {
+                'state': 'close',
+                })
+        journal_periods = JournalPeriod.search([
+            ('period', 'in', [p.id for p in periods]),
             ])
-        journal_period_obj.close(journal_period_ids)
+        JournalPeriod.close(journal_periods)
 
-    def open_(self, ids):
+    @classmethod
+    def open_(cls, periods):
         "Open Journal"
-        self.write(ids, {
+        cls.write(periods, {
                 'state': 'open',
                 })
 
-Period()
-
 
 class ClosePeriod(Wizard):
     'Close Period'
-    _name = 'account.period.close'
+    __name__ = 'account.period.close'
     start_state = 'close'
     close = StateTransition()
 
-    def transition_close(self, session):
-        period_obj = Pool().get('account.period')
-        period_obj.close(Transaction().context['active_ids'])
+    def transition_close(self):
+        Period = Pool().get('account.period')
+        Period.close(Period.browse(Transaction().context['active_ids']))
         return 'end'
 
-ClosePeriod()
-
 
 class ReOpenPeriod(Wizard):
     'Re-Open Period'
-    _name = 'account.period.reopen'
+    __name__ = 'account.period.reopen'
     start_state = 'reopen'
     reopen = StateTransition()
 
-    def transition_reopen(self, session):
-        period_obj = Pool().get('account.period')
-        period_obj.open_(Transaction().context['active_ids'])
+    def transition_reopen(self):
+        Period = Pool().get('account.period')
+        Period.open_(Period.browse(Transaction().context['active_ids']))
         return 'end'
-
-ReOpenPeriod()
diff -r f40675df5d4c trytond/modules/account/tax.py
--- a/trytond/modules/account/tax.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/tax.py	Mon Sep 24 20:36:38 2012 +0200
@@ -6,35 +6,37 @@
 from trytond.backend import TableHandler
 from trytond.pyson import Eval, If, Bool, PYSONEncoder
 from trytond.transaction import Transaction
-from trytond.cache import Cache
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.config import CONFIG
 
+__all__ = ['TaxGroup', 'TaxCodeTemplate', 'TaxCode',
+    'OpenChartTaxCodeStart', 'OpenChartTaxCode',
+    'TaxTemplate', 'Tax', 'TaxLine', 'TaxRuleTemplate', 'TaxRule',
+    'TaxRuleLineTemplate', 'TaxRuleLine',
+    'OpenTaxCode',
+    'AccountTemplateTaxTemplate', 'AccountTemplate2', 'AccountTax', 'Account2']
+__metaclass__ = PoolMeta
 
-class Group(ModelSQL, ModelView):
+
+class TaxGroup(ModelSQL, ModelView):
     'Tax Group'
-    _name = 'account.tax.group'
-    _description = __doc__
-
+    __name__ = 'account.tax.group'
     name = fields.Char('Name', size=None, required=True, translate=True)
     code = fields.Char('Code', size=None, required=True)
 
-    def init(self, module_name):
-        super(Group, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(TaxGroup, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.4 drop code_uniq constraint
         table.drop_constraint('code_uniq')
 
-Group()
 
-
-class CodeTemplate(ModelSQL, ModelView):
+class TaxCodeTemplate(ModelSQL, ModelView):
     'Tax Code Template'
-    _name = 'account.tax.code.template'
-    _description = __doc__
-
+    __name__ = 'account.tax.code.template'
     name = fields.Char('Name', required=True, translate=True)
     code = fields.Char('Code')
     parent = fields.Many2One('account.tax.code.template', 'Parent')
@@ -43,104 +45,88 @@
             domain=[('parent', '=', None)], required=True)
     description = fields.Text('Description', translate=True)
 
-    def __init__(self):
-        super(CodeTemplate, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(TaxCodeTemplate, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_tax_code'),
-        ]
-        self._error_messages.update({
-            'recursive_tax_code': 'You can not create recursive tax codes!',
-        })
-        self._order.insert(0, ('code', 'ASC'))
-        self._order.insert(0, ('account', 'ASC'))
+            ]
+        cls._error_messages.update({
+                'recursive_tax_code': \
+                    'You can not create recursive tax codes!',
+                })
+        cls._order.insert(0, ('code', 'ASC'))
+        cls._order.insert(0, ('account', 'ASC'))
 
-    def _get_tax_code_value(self, template, code=None):
+    def _get_tax_code_value(self, code=None):
         '''
         Set values for tax code creation.
-
-        :param template: the BrowseRecord of the template
-        :param code: the BrowseRecord of the code to update
-        :return: a dictionary with tax code fields as key and values as value
         '''
         res = {}
-        if not code or code.name != template.name:
-            res['name'] = template.name
-        if not code or code.code != template.code:
-            res['code'] = template.code
-        if not code or code.description != template.description:
-            res['description'] = template.description
-        if not code or code.template.id != template.id:
-            res['template'] = template.id
+        if not code or code.name != self.name:
+            res['name'] = self.name
+        if not code or code.code != self.code:
+            res['code'] = self.code
+        if not code or code.description != self.description:
+            res['description'] = self.description
+        if not code or code.template.id != self.id:
+            res['self'] = self.id
         return res
 
-    def create_tax_code(self, template, company_id, template2tax_code=None,
+    def create_tax_code(self, company_id, template2tax_code=None,
             parent_id=None):
         '''
         Create recursively tax codes based on template.
-
-        :param template: the template id or the BrowseRecord of template
-                used for tax code creation
-        :param company_id: the id of the company for which tax codes are
-                created
-        :param template2tax_code: a dictionary with tax code template id as key
-                and tax code id as value, used to convert template id into
-                tax code. The dictionary is filled with new tax codes
-        :param parent_id: the tax code id of the parent of the tax codes that
-                must be created
-        :return: id of the tax code created
+        template2tax_code is a dictionary with tax code template id as key and
+        tax code id as value, used to convert template id into tax code. The
+        dictionary is filled with new tax codes.
+        Return the id of the tax code created
         '''
-        tax_code_obj = Pool().get('account.tax.code')
-        lang_obj = Pool().get('ir.lang')
+        pool = Pool()
+        TaxCode = pool.get('account.tax.code')
+        Lang = pool.get('ir.lang')
 
         if template2tax_code is None:
             template2tax_code = {}
 
-        if isinstance(template, (int, long)):
-            template = self.browse(template)
-
-        if template.id not in template2tax_code:
-            vals = self._get_tax_code_value(template)
+        if self.id not in template2tax_code:
+            vals = self._get_tax_code_value()
             vals['company'] = company_id
             vals['parent'] = parent_id
 
-            new_id = tax_code_obj.create(vals)
+            new_tax_code = TaxCode.create(vals)
 
-            prev_lang = template._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in template._columns.iteritems():
+            for field_name, field in self._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                template.setLang(lang)
-                data = {}
-                for field_name, field in template._columns.iteritems():
-                    if getattr(field, 'translate', False) \
-                            and template[field_name] != prev_data[field_name]:
-                        data[field_name] = template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        tax_code_obj.write(new_id, data)
-            template.setLang(prev_lang)
-            template2tax_code[template.id] = new_id
-        else:
-            new_id = template2tax_code[template.id]
+                with Transaction().set_context(language=lang):
+                    template = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in template._fields.iteritems():
+                        if (getattr(field, 'translate', False)
+                                and (getattr(template, field_name) !=
+                                    prev_data[field_name])):
+                            data[field_name] = getattr(template, field_name)
+                    if data:
+                        TaxCode.write([new_tax_code], data)
+            template2tax_code[self.id] = new_tax_code.id
+        new_id = template2tax_code[self.id]
 
         new_childs = []
         for child in template.childs:
-            new_childs.append(self.create_tax_code(child, company_id,
-                template2tax_code=template2tax_code, parent_id=new_id))
+            new_childs.append(child.create_tax_code(company_id,
+                    template2tax_code=template2tax_code, parent_id=new_id))
         return new_id
 
-CodeTemplate()
 
-
-class Code(ModelSQL, ModelView):
+class TaxCode(ModelSQL, ModelView):
     'Tax Code'
-    _name = 'account.tax.code'
-    _description = __doc__
-
+    __name__ = 'account.tax.code'
     name = fields.Char('Name', size=None, required=True, select=True,
                        translate=True)
     code = fields.Char('Code', size=None, select=True)
@@ -152,51 +138,50 @@
     childs = fields.One2Many('account.tax.code', 'parent', 'Children',
             domain=[('company', '=', Eval('company', 0))], depends=['company'])
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['company']), 'get_currency_digits')
+        on_change_with=['company']), 'on_change_with_currency_digits')
     sum = fields.Function(fields.Numeric('Sum',
         digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']),
         'get_sum')
     template = fields.Many2One('account.tax.code.template', 'Template')
     description = fields.Text('Description', translate=True)
 
-    def __init__(self):
-        super(Code, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(TaxCode, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_tax_code'),
-        ]
-        self._error_messages.update({
-            'recursive_tax_code': 'You can not create recursive tax codes!',
-        })
-        self._order.insert(0, ('code', 'ASC'))
+            ]
+        cls._error_messages.update({
+                'recursive_tax_code': \
+                    'You can not create recursive tax codes!',
+                })
+        cls._order.insert(0, ('code', 'ASC'))
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def on_change_with_currency_digits(self, vals):
-        company_obj = Pool().get('company.company')
-        if vals.get('company'):
-            company = company_obj.browse(vals['company'])
-            return company.currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.company:
+            return self.company.currency.digits
         return 2
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for code in self.browse(ids):
-            res[code.id] = code.company.currency.digits
-        return res
-
-    def get_sum(self, ids, name):
+    @classmethod
+    def get_sum(cls, codes, name):
         cursor = Transaction().cursor
         res = {}
-        move_line_obj = Pool().get('account.move.line')
-        currency_obj = Pool().get('currency.currency')
+        pool = Pool()
+        MoveLine = pool.get('account.move.line')
 
-        child_ids = self.search([('parent', 'child_of', ids)])
-        all_ids = {}.fromkeys(ids + child_ids).keys()
-        line_query, _ = move_line_obj.query_get()
+        childs = cls.search([
+                ('parent', 'child_of', [c.id for c in codes]),
+                ])
+        all_codes = list(set(codes) | set(childs))
+        line_query, _ = MoveLine.query_get()
         cursor.execute('SELECT c.id, SUM(tl.amount) '
             'FROM account_tax_code c, '
                 'account_tax_line tl, '
@@ -204,10 +189,10 @@
             'WHERE c.id = tl.code '
                 'AND tl.move_line = l.id '
                 'AND c.id IN (' +
-                    ','.join(('%s',) * len(all_ids)) + ') '
+                    ','.join(('%s',) * len(all_codes)) + ') '
                 'AND ' + line_query + ' '
                 'AND c.active '
-            'GROUP BY c.id', all_ids)
+            'GROUP BY c.id', [c.id for c in all_codes])
         code_sum = {}
         for code_id, sum in cursor.fetchall():
             # SQLite uses float for SUM
@@ -215,97 +200,82 @@
                 sum = Decimal(str(sum))
             code_sum[code_id] = sum
 
-        for code in self.browse(ids):
+        for code in codes:
             res.setdefault(code.id, Decimal('0.0'))
-            child_ids = self.search([
-                ('parent', 'child_of', [code.id]),
-                ])
-            for child_id in child_ids:
-                res[code.id] += currency_obj.round(code.company.currency,
-                        code_sum.get(child_id, Decimal('0.0')))
-            res[code.id] = currency_obj.round(code.company.currency,
-                    res[code.id])
+            childs = cls.search([
+                    ('parent', 'child_of', [code.id]),
+                    ])
+            for child in childs:
+                res[code.id] += code.company.currency.round(
+                    code_sum.get(child.id, Decimal('0.0')))
+            res[code.id] = code.company.currency.round(res[code.id])
         return res
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for code in self.browse(ids):
-            if code.code:
-                res[code.id] = code.code + ' - ' + code.name
-            else:
-                res[code.id] = code.name
-        return res
+    def get_rec_name(self, name):
+        if self.code:
+            return self.code + ' - ' + self.name
+        else:
+            return self.name
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([('code',) + tuple(clause[1:])], limit=1, order=[])
-        if ids:
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        codes = cls.search([('code',) + tuple(clause[1:])], limit=1, order=[])
+        if codes:
             return [('code',) + tuple(clause[1:])]
         return [('name',) + tuple(clause[1:])]
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        code_ids = self.search([
-            ('parent', 'child_of', ids),
-            ])
-        return super(Code, self).delete(code_ids)
+    @classmethod
+    def delete(cls, codes):
+        codes = cls.search([
+                ('parent', 'child_of', [c.id for c in codes]),
+                ])
+        super(TaxCode, cls).delete(codes)
 
-    def update_tax_code(self, code, template2tax_code=None):
+    def update_tax_code(self, template2tax_code=None):
         '''
         Update recursively tax code based on template.
-
-        :param code: a code id or the BrowseRecord of the code
-        :param template2tax_code: a dictionary with tax code template id as key
-                and tax code id as value, used to convert template id into
-                tax code. The dictionary is filled with new tax codes
+        template2tax_code is a dictionary with tax code template id as key and
+        tax code id as value, used to convert template id into tax code. The
+        dictionary is filled with new tax codes
         '''
-        template_obj = Pool().get('account.tax.code.template')
-        lang_obj = Pool().get('ir.lang')
+        Lang = Pool().get('ir.lang')
 
         if template2tax_code is None:
             template2tax_code = {}
 
-        if isinstance(code, (int, long)):
-            code = self.browse(code)
+        if self.template:
+            vals = self.template._get_tax_code_value(code=self)
+            if vals:
+                self.write([self], vals)
 
-        if code.template:
-            vals = template_obj._get_tax_code_value(code.template, code=code)
-            if vals:
-                self.write(code.id, vals)
-
-            prev_lang = code._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in code.template._columns.iteritems():
+            for field_name, field in self.template._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = code.template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self.template, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                code.setLang(lang)
-                data = {}
-                for field_name, field in code.template._columns.iteritems():
-                    if (getattr(field, 'translate', False)
-                            and code.template[field_name] !=
-                            prev_data[field_name]):
-                        data[field_name] = code.template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        self.write(code.id, data)
-            code.setLang(prev_lang)
-            template2tax_code[code.template.id] = code.id
+                with Transaction().set_context(language=lang):
+                    code = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in code.template._fields.iteritems():
+                        if (getattr(field, 'translate', False)
+                                and (getattr(code.template, field_name) !=
+                                    prev_data[field_name])):
+                            data[field_name] = getattr(code.template,
+                                field_name)
+                    if data:
+                        self.write([code], data)
+            template2tax_code[self.template.id] = self.id
 
         for child in code.childs:
-            self.update_tax_code(child, template2tax_code=template2tax_code)
-
-Code()
+            child.update_tax_code(template2tax_code=template2tax_code)
 
 
 class OpenChartTaxCodeStart(ModelView):
     'Open Chart of Tax Codes'
-    _name = 'account.tax.code.open_chart.start'
-    _description = __doc__
+    __name__ = 'account.tax.code.open_chart.start'
     method = fields.Selection([
         ('fiscalyear', 'By Fiscal Year'),
         ('periods', 'By Periods'),
@@ -321,16 +291,14 @@
             'invisible': Eval('method') != 'periods',
             }, depends=['method'])
 
-    def default_method(self):
+    @staticmethod
+    def default_method():
         return 'periods'
 
-OpenChartTaxCodeStart()
-
 
 class OpenChartTaxCode(Wizard):
     'Open Chart of Tax Codes'
-    _name = 'account.tax.code.open_chart'
-
+    __name__ = 'account.tax.code.open_chart'
     start = StateView('account.tax.code.open_chart.start',
         'account.tax_code_open_chart_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -338,28 +306,24 @@
             ])
     open_ = StateAction('account.act_tax_code_tree2')
 
-    def do_open_(self, session, action):
-        if session.start.method == 'fiscalyear':
+    def do_open_(self, action):
+        if self.start.method == 'fiscalyear':
             action['pyson_context'] = PYSONEncoder().encode({
-                    'fiscalyear': session.start.fiscalyear.id,
+                    'fiscalyear': self.start.fiscalyear.id,
                     })
         else:
             action['pyson_context'] = PYSONEncoder().encode({
-                    'periods': [x.id for x in session.start.periods],
+                    'periods': [x.id for x in self.start.periods],
                     })
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
 
-OpenChartTaxCode()
-
 
 class TaxTemplate(ModelSQL, ModelView):
     'Account Tax Template'
-    _name = 'account.tax.template'
-    _description = __doc__
-
+    __name__ = 'account.tax.template'
     name = fields.Char('Name', required=True, translate=True)
     description = fields.Char('Description', required=True, translate=True)
     group = fields.Many2One('account.tax.group', 'Group')
@@ -396,15 +360,17 @@
     account = fields.Many2One('account.account.template', 'Account Template',
             domain=[('parent', '=', None)], required=True)
 
-    def __init__(self):
-        super(TaxTemplate, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._order.insert(0, ('account', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(TaxTemplate, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._order.insert(0, ('account', 'ASC'))
 
-    def init(self, module_name):
-        super(TaxTemplate, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(TaxTemplate, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 group is no more required
         table.not_null_action('group', action='remove')
@@ -412,143 +378,134 @@
         #Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'percentage'
 
-    def default_include_base_amount(self):
+    @staticmethod
+    def default_include_base_amount():
         return False
 
-    def default_invoice_base_sign(self):
+    @staticmethod
+    def default_invoice_base_sign():
         return Decimal('1')
 
-    def default_invoice_tax_sign(self):
+    @staticmethod
+    def default_invoice_tax_sign():
         return Decimal('1')
 
-    def default_credit_note_base_sign(self):
+    @staticmethod
+    def default_credit_note_base_sign():
         return Decimal('1')
 
-    def default_credit_note_tax_sign(self):
+    @staticmethod
+    def default_credit_note_tax_sign():
         return Decimal('1')
 
-    def _get_tax_value(self, template, tax=None):
+    def _get_tax_value(self, tax=None):
         '''
         Set values for tax creation.
-
-        :param template: the BrowseRecord of the template
-        :param tax: the BrowseRecord of the tax to update
-        :return: a dictionary with account fields as key and values as value
         '''
         res = {}
         for field in ('name', 'description', 'sequence', 'amount',
                 'percentage', 'type', 'invoice_base_sign', 'invoice_tax_sign',
                 'credit_note_base_sign', 'credit_note_tax_sign'):
-            if not tax or tax[field] != template[field]:
-                res[field] = template[field]
+            if not tax or tax[field] != getattr(self, field):
+                res[field] = getattr(self, field)
         for field in ('group',):
-            if not tax or tax[field].id != template[field].id:
-                res[field] = template[field].id
-        if not tax or tax.template.id != template.id:
-            res['template'] = template.id
+            if not tax or tax[field].id != getattr(self, field).id:
+                res[field] = getattr(self, field).id
+        if not tax or tax.template.id != self.id:
+            res['template'] = self.id
         return res
 
-    def create_tax(self, template, company_id, template2tax_code,
-            template2account, template2tax=None, parent_id=None):
+    def create_tax(self, company_id, template2tax_code, template2account,
+            template2tax=None, parent_id=None):
         '''
         Create recursively taxes based on template.
 
-        :param template: the template id or the BrowseRecord of template
-                used for tax creation
-        :param company_id: the id of the company for which taxes are created
-        :param template2tax_code: a dictionary with tax code template id as key
-                and tax code id as value, used to convert tax code template
-                into tax code
-        :param template2account: a dictionary with account template id as key
-                and account id as value, used to convert account template into
-                account code
-        :param template2tax: a dictionary with tax template id as key and
-                tax id as value, used to convert template id into tax.
-                The dictionary is filled with new taxes
-        :param parent_id: the tax id of the parent of the tax that must be
-                created
-        :return: id of the tax created
+        template2tax_code is a dictionary with tax code template id as key and
+        tax code id as value, used to convert tax code template into tax code.
+        template2account is a dictionary with account template id as key and
+        account id as value, used to convert account template into account
+        code.
+        template2tax is a dictionary with tax template id as key and tax id as
+        value, used to convert template id into tax.  The dictionary is filled
+        with new taxes.
+        Return id of the tax created
         '''
-        tax_obj = Pool().get('account.tax')
-        lang_obj = Pool().get('ir.lang')
+        pool = Pool()
+        Tax = pool.get('account.tax')
+        Lang = pool.get('ir.lang')
 
         if template2tax is None:
             template2tax = {}
 
-        if isinstance(template, (int, long)):
-            template = self.browse(template)
-
-        if template.id not in template2tax:
-            vals = self._get_tax_value(template)
+        if self.id not in template2tax:
+            vals = self._get_tax_value()
             vals['company'] = company_id
             vals['parent'] = parent_id
-            if template.invoice_account:
+            if self.invoice_account:
                 vals['invoice_account'] = \
-                        template2account[template.invoice_account.id]
+                        template2account[self.invoice_account.id]
             else:
                 vals['invoice_account'] = None
-            if template.credit_note_account:
+            if self.credit_note_account:
                 vals['credit_note_account'] = \
-                        template2account[template.credit_note_account.id]
+                        template2account[self.credit_note_account.id]
             else:
                 vals['credit_note_account'] = None
-            if template.invoice_base_code:
+            if self.invoice_base_code:
                 vals['invoice_base_code'] = \
-                        template2tax_code[template.invoice_base_code.id]
+                        template2tax_code[self.invoice_base_code.id]
             else:
                 vals['invoice_base_code'] = None
-            if template.invoice_tax_code:
+            if self.invoice_tax_code:
                 vals['invoice_tax_code'] = \
-                        template2tax_code[template.invoice_tax_code.id]
+                        template2tax_code[self.invoice_tax_code.id]
             else:
                 vals['invoice_tax_code'] = None
-            if template.credit_note_base_code:
+            if self.credit_note_base_code:
                 vals['credit_note_base_code'] = \
-                        template2tax_code[template.credit_note_base_code.id]
+                        template2tax_code[self.credit_note_base_code.id]
             else:
                 vals['credit_note_base_code'] = None
-            if template.credit_note_tax_code:
+            if self.credit_note_tax_code:
                 vals['credit_note_tax_code'] = \
-                        template2tax_code[template.credit_note_tax_code.id]
+                        template2tax_code[self.credit_note_tax_code.id]
             else:
                 vals['credit_note_tax_code'] = None
 
-            new_id = tax_obj.create(vals)
+            new_tax = Tax.create(vals)
 
-            prev_lang = template._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in template._columns.iteritems():
+            for field_name, field in self._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                template.setLang(lang)
-                data = {}
-                for field_name, field in template._columns.iteritems():
-                    if getattr(field, 'translate', False) \
-                            and template[field_name] != prev_data[field_name]:
-                        data[field_name] = template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        tax_obj.write(new_id, data)
-            template.setLang(prev_lang)
-            template2tax[template.id] = new_id
-        else:
-            new_id = template2tax[template.id]
+                with Transaction().set_context(language=lang):
+                    template = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in template._fields.iteritems():
+                        if (getattr(field, 'translate', False)
+                                and (getattr(template, field_name)
+                                    != prev_data[field_name])):
+                            data[field_name] = getattr(template, field_name)
+                    if data:
+                        Tax.write([new_tax], data)
+            template2tax[self.id] = new_tax.id
+        new_id = template2tax[self.id]
 
         new_childs = []
-        for child in template.childs:
-            new_childs.append(self.create_tax(child, company_id,
-                template2tax_code, template2account, template2tax=template2tax,
-                parent_id=new_id))
+        for child in self.childs:
+            new_childs.append(child.create_tax(company_id, template2tax_code,
+                    template2account, template2tax=template2tax,
+                    parent_id=new_id))
         return new_id
 
-TaxTemplate()
-
 
 class Tax(ModelSQL, ModelView):
     '''
@@ -559,9 +516,7 @@
         fixed: tax = amount
         none: tax = none
     '''
-    _name = 'account.tax'
-    _description = 'Account Tax'
-
+    __name__ = 'account.tax'
     name = fields.Char('Name', required=True, translate=True)
     description = fields.Char('Description', required=True, translate=True,
             help="The name that will be used in reports")
@@ -575,7 +530,7 @@
         '%(table)s.sequence %(order)s',
         help='Use to order the taxes')
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['company']), 'get_currency_digits')
+        on_change_with=['company']), 'on_change_with_currency_digits')
     amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
         states={
             'required': Eval('type') == 'fixed',
@@ -666,14 +621,16 @@
             }, depends=['type'])
     template = fields.Many2One('account.tax.template', 'Template')
 
-    def __init__(self):
-        super(Tax, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(Tax, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
-        super(Tax, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Tax, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 group is no more required
         table.not_null_action('group', action='remove')
@@ -681,96 +638,78 @@
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'percentage'
 
-    def default_include_base_amount(self):
+    @staticmethod
+    def default_include_base_amount():
         return False
 
-    def default_invoice_base_sign(self):
+    @staticmethod
+    def default_invoice_base_sign():
         return Decimal('1')
 
-    def default_invoice_tax_sign(self):
+    @staticmethod
+    def default_invoice_tax_sign():
         return Decimal('1')
 
-    def default_credit_note_base_sign(self):
+    @staticmethod
+    def default_credit_note_base_sign():
         return Decimal('1')
 
-    def default_credit_note_tax_sign(self):
+    @staticmethod
+    def default_credit_note_tax_sign():
         return Decimal('1')
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def on_change_with_currency_digits(self, vals):
-        company_obj = Pool().get('company.company')
-        if vals.get('company'):
-            company = company_obj.browse(vals['company'])
-            return company.currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.company:
+            return self.company.currency.digits
         return 2
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for tax in self.browse(ids):
-            res[tax.id] = tax.company.currency.digits
-        return res
-
-    def _process_tax(self, tax, price_unit):
-        if tax.type == 'percentage':
-            amount = price_unit * tax.percentage / Decimal('100')
+    def _process_tax(self, price_unit):
+        if self.type == 'percentage':
+            amount = price_unit * self.percentage / Decimal('100')
             return {
                 'base': price_unit,
                 'amount': amount,
-                'tax': tax,
-            }
-        if tax.type == 'fixed':
-            amount = tax.amount
+                'tax': self,
+                }
+        if self.type == 'fixed':
+            amount = self.amount
             return {
                 'base': price_unit,
                 'amount': amount,
-                'tax': tax,
-            }
+                'tax': self,
+                }
 
-    def _unit_compute(self, taxes, price_unit):
+    @classmethod
+    def _unit_compute(cls, taxes, price_unit):
         res = []
         for tax in taxes:
             if tax.type != 'none':
-                res.append(self._process_tax(tax, price_unit))
+                res.append(tax._process_tax(price_unit))
             if len(tax.childs):
-                res.extend(self._unit_compute(tax.childs, price_unit))
+                res.extend(cls._unit_compute(tax.childs, price_unit))
         return res
 
-    def delete(self, ids):
-        # Restart the cache
-        self.sort_taxes.reset()
-        return super(Tax, self).delete(ids)
+    @classmethod
+    def sort_taxes(cls, taxes):
+        '''
+        Return a list of taxes sorted
+        '''
+        return sorted(taxes, key=lambda t: (t.sequence, t.id))
 
-    def create(self, vals):
-        # Restart the cache
-        self.sort_taxes.reset()
-        return super(Tax, self).create(vals)
-
-    def write(self, ids, vals):
-        # Restart the cache
-        self.sort_taxes.reset()
-        return super(Tax, self).write(ids, vals)
-
-    @Cache('account_tax.sort_taxes')
-    def sort_taxes(self, ids):
-        '''
-        Return a list of taxe ids sorted
-
-        :param ids: a list of tax ids
-        :return: a list of tax ids sorted
-        '''
-        return self.search([
-            ('id', 'in', ids),
-            ], order=[('sequence', 'ASC'), ('id', 'ASC')])
-
-    def compute(self, ids, price_unit, quantity):
+    @classmethod
+    def compute(cls, taxes, price_unit, quantity):
         '''
         Compute taxes for price_unit and quantity.
         Return list of dict for each taxes and their childs with:
@@ -778,130 +717,118 @@
             amount
             tax
         '''
-        ids = self.sort_taxes(ids)
-        taxes = self.browse(ids)
-        res = self._unit_compute(taxes, price_unit)
+        taxes = cls.sort_taxes(taxes)
+        res = cls._unit_compute(taxes, price_unit)
         quantity = Decimal(str(quantity or 0.0))
         for row in res:
             row['base'] *= quantity
             row['amount'] *= quantity
         return res
 
-    def update_tax(self, tax, template2tax_code, template2account,
+    def update_tax(self, template2tax_code, template2account,
             template2tax=None):
         '''
         Update recursively taxes based on template.
-
-        :param tax: a tax id or the BrowseRecord of the tax
-        :param template2tax_code: a dictionary with tax code template id as key
-                and tax code id as value, used to convert tax code template
-                into tax code
-        :param template2account: a dictionary with account template id as key
-                and account id as value, used to convert account template into
-                account code
-        :param template2tax: a dictionary with tax template id as key and
-                tax id as value, used to convert template id into tax.
-                The dictionary is filled with new taxes
+        template2tax_code is a dictionary with tax code template id as key and
+        tax code id as value, used to convert tax code template into tax code.
+        template2account is a dictionary with account template id as key and
+        account id as value, used to convert account template into account
+        code.
+        template2tax is a dictionary with tax template id as key and tax id as
+        value, used to convert template id into tax.  The dictionary is filled
+        with new taxes.
         '''
-        template_obj = Pool().get('account.tax.template')
-        lang_obj = Pool().get('ir.lang')
+        Lang = Pool().get('ir.lang')
 
         if template2tax is None:
             template2tax = {}
 
-        if isinstance(tax, (int, long)):
-            tax = self.browse(tax)
-
-        if tax.template:
-            vals = template_obj._get_tax_value(tax.template, tax=tax)
-            if (tax.template.invoice_account
-                    and tax.invoice_account.id != template2account.get(
-                        tax.template.invoice_account.id)):
+        if self.template:
+            vals = self.template._get_tax_value(tax=self)
+            if (self.template.invoice_account
+                    and self.invoice_account.id != template2account.get(
+                        self.template.invoice_account.id)):
                 vals['invoice_account'] = template2account.get(
-                    tax.template.invoice_account.id)
-            elif (not tax.template.invoice_account
-                    and tax.invoice_account):
+                    self.template.invoice_account.id)
+            elif (not self.template.invoice_account
+                    and self.invoice_account):
                 vals['invoice_account'] = None
-            if (tax.template.credit_note_account
-                    and tax.credit_note_account.id != template2account.get(
-                        tax.template.credit_note_account.id)):
+            if (self.template.credit_note_account
+                    and self.credit_note_account.id != template2account.get(
+                        self.template.credit_note_account.id)):
                 vals['credit_note_account'] = template2account.get(
-                    tax.template.credit_note_account.id)
-            elif (not tax.template.credit_note_account
-                    and tax.credit_note_account):
+                    self.template.credit_note_account.id)
+            elif (not self.template.credit_note_account
+                    and self.credit_note_account):
                 vals['credit_note_account'] = None
-            if (tax.template.invoice_base_code
-                    and tax.invoice_base_code.id != template2tax_code.get(
-                        tax.template.invoice_base_code.id)):
+            if (self.template.invoice_base_code
+                    and self.invoice_base_code.id != template2tax_code.get(
+                        self.template.invoice_base_code.id)):
                 vals['invoice_base_code'] = template2tax_code.get(
-                    tax.template.invoice_base_code.id)
-            elif (not tax.template.invoice_base_code
-                    and tax.invoice_base_code):
+                    self.template.invoice_base_code.id)
+            elif (not self.template.invoice_base_code
+                    and self.invoice_base_code):
                 vals['invoice_base_code'] = None
-            if (tax.template.invoice_tax_code
-                    and tax.invoice_tax_code.id != template2tax_code.get(
-                        tax.template.invoice_tax_code.id)):
+            if (self.template.invoice_tax_code
+                    and self.invoice_tax_code.id != template2tax_code.get(
+                        self.template.invoice_tax_code.id)):
                 vals['invoice_tax_code'] = template2tax_code.get(
-                    tax.template.invoice_tax_code.id)
-            elif (not tax.template.invoice_tax_code
-                    and tax.invoice_tax_code):
+                    self.template.invoice_tax_code.id)
+            elif (not self.template.invoice_tax_code
+                    and self.invoice_tax_code):
                 vals['invoice_tax_code'] = None
-            if (tax.template.credit_note_base_code
-                    and tax.credit_note_base_code.id != template2tax_code.get(
-                        tax.template.credit_note_base_code.id)):
+            if (self.template.credit_note_base_code
+                    and self.credit_note_base_code.id != template2tax_code.get(
+                        self.template.credit_note_base_code.id)):
                 vals['credit_note_base_code'] = template2tax_code.get(
-                    tax.template.credit_note_base_code.id)
-            elif (not tax.template.credit_note_base_code
-                    and tax.credit_note_base_code):
+                    self.template.credit_note_base_code.id)
+            elif (not self.template.credit_note_base_code
+                    and self.credit_note_base_code):
                 vals['credit_note_base_code'] = None
-            if (tax.template.credit_note_tax_code
-                    and tax.credit_note_tax_code.id != template2tax_code.get(
-                        tax.template.credit_note_tax_code.id)):
+            if (self.template.credit_note_tax_code
+                    and self.credit_note_tax_code.id != template2tax_code.get(
+                        self.template.credit_note_tax_code.id)):
                 vals['credit_note_tax_code'] = template2tax_code.get(
-                    tax.template.credit_note_tax_code.id)
-            elif (not tax.template.credit_note_tax_code
-                    and tax.credit_note_tax_code):
+                    self.template.credit_note_tax_code.id)
+            elif (not self.template.credit_note_tax_code
+                    and self.credit_note_tax_code):
                 vals['credit_note_tax_code'] = None
 
             if vals:
-                self.write(tax.id, vals)
+                self.write([self], vals)
 
-            prev_lang = tax._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in tax.template._columns.iteritems():
+            for field_name, field in self.template._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = tax.template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self.template, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                tax.setLang(lang)
-                data = {}
-                for field_name, field in tax.template._columns.iteritems():
-                    if (getattr(field, 'translate', False)
-                            and tax.template[field_name] !=
-                            prev_data[field_name]):
-                        data[field_name] = tax.template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        self.write(tax.id, data)
-            tax.setLang(prev_lang)
+                with Transaction().set_context(language=lang):
+                    tax = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in tax.template._fields.iteritems():
+                        if (getattr(field, 'translate', False)
+                                and (getattr(tax.template, field_name)
+                                    != prev_data[field_name])):
+                            data[field_name] = getattr(tax.template,
+                                field_name)
+                    if data:
+                        self.write([tax], data)
             template2tax[tax.template.id] = tax.id
 
         for child in tax.childs:
-            self.update_tax(child, template2tax_code, template2account,
-                    template2tax=template2tax)
+            child.update_tax(template2tax_code, template2account,
+                template2tax=template2tax)
 
-Tax()
 
-
-class Line(ModelSQL, ModelView):
+class TaxLine(ModelSQL, ModelView):
     'Tax Line'
-    _name = 'account.tax.line'
-    _description = __doc__
+    __name__ = 'account.tax.line'
     _rec_name = 'amount'
-
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['move_line']), 'get_currency_digits')
+        on_change_with=['move_line']), 'on_change_with_currency_digits')
     amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
         required=True, depends=['currency_digits'])
     code = fields.Many2One('account.tax.code', 'Code', select=True,
@@ -911,109 +838,81 @@
     move_line = fields.Many2One('account.move.line', 'Move Line',
             required=True, select=True, ondelete='CASCADE')
 
-    def on_change_with_currency_digits(self, vals):
-        move_line_obj = Pool().get('account.move.line')
-        if vals.get('move_line'):
-            move_line = move_line_obj.browse(vals['move_line'])
-            return move_line.currency_digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.move_line:
+            return self.move_line.currency_digits
         return 2
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            res[line.id] = line.move_line.currency_digits
-        return res
-
-    def on_change_tax(self, vals):
-        res = {
+    def on_change_tax(self):
+        return {
             'code': None,
             }
-        return res
 
-Line()
 
-
-class RuleTemplate(ModelSQL, ModelView):
+class TaxRuleTemplate(ModelSQL, ModelView):
     'Tax Rule Template'
-    _name = 'account.tax.rule.template'
-    _description = __doc__
+    __name__ = 'account.tax.rule.template'
     name = fields.Char('Name', required=True, translate=True)
     lines = fields.One2Many('account.tax.rule.line.template', 'rule', 'Lines')
     account = fields.Many2One('account.account.template', 'Account Template',
             domain=[('parent', '=', None)], required=True)
 
-    def _get_tax_rule_value(self, template, rule=None):
+    def _get_tax_rule_value(self, rule=None):
         '''
         Set values for tax rule creation.
-
-        :param template: the BrowseRecord of the template
-        :param rule: the BrowseRecord of the rule to update
-        :return: a dictionary with rule fields as key and values as value
         '''
         res = {}
-        if not rule or rule.name != template.name:
-            res['name'] = template.name
-        if not rule or rule.template.id != template.id:
-            res['template'] = template.id
+        if not rule or rule.name != self.name:
+            res['name'] = self.name
+        if not rule or rule.template.id != self.id:
+            res['template'] = self.id
         return res
 
-    def create_rule(self, template, company_id, template2rule=None):
+    def create_rule(self, company_id, template2rule=None):
         '''
         Create tax rule based on template.
-
-        :param template: the template id or the BrowseRecord of template
-                used for tax rule creation
-        :param company_id: the id of the company for which tax rules are
-                created
-        :param template2rule: a dictionary with tax rule template id as key
-                and tax rule id as value, used to convert template id into
-                tax rule. The dictionary is filled with new tax rules
-        :return: id of the tax rule created
+        template2rule is a dictionary with tax rule template id as key and tax
+        rule id as value, used to convert template id into tax rule. The
+        dictionary is filled with new tax rules.
+        Return id of the tax rule created
         '''
-        rule_obj = Pool().get('account.tax.rule')
-        lang_obj = Pool().get('ir.lang')
+        pool = Pool()
+        Rule = pool.get('account.tax.rule')
+        Lang = pool.get('ir.lang')
 
         if template2rule is None:
             template2rule = {}
 
-        if isinstance(template, (int, long)):
-            template = self.browse(template)
+        if self.id not in template2rule:
+            vals = self._get_tax_rule_value()
+            vals['company'] = company_id
+            new_rule = Rule.create(vals)
 
-        if template.id not in template2rule:
-            vals = self._get_tax_rule_value(template)
-            vals['company'] = company_id
-            new_id = rule_obj.create(vals)
-
-            prev_lang = template._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in template._columns.iteritems():
+            for field_name, field in self._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                template.setLang(lang)
-                data = {}
-                for field_name, field in template._columns.iteritems():
-                    if getattr(field, 'translate', False) \
-                            and template[field_name] != prev_data[field_name]:
-                        data[field_name] = template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        rule_obj.write(new_id, data)
-            template.setLang(prev_lang)
-            template2rule[template.id] = new_id
-        else:
-            new_id = template2rule[template.id]
-        return new_id
+                with Transaction().set_context(language=lang):
+                    template = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in template._fields.iteritems():
+                        if (getattr(field, 'translate', False)
+                                and (getattr(self, field_name)
+                                    != prev_data[field_name])):
+                            data[field_name] = getattr(self, field_name)
+                    if data:
+                        Rule.write([new_rule], data)
+            template2rule[self.id] = new_rule.id
+        return template2rule[self.id]
 
-RuleTemplate()
 
-
-class Rule(ModelSQL, ModelView):
+class TaxRule(ModelSQL, ModelView):
     'Tax Rule'
-    _name = 'account.tax.rule'
-    _description = __doc__
+    __name__ = 'account.tax.rule'
     name = fields.Char('Name', required=True, translate=True)
     company = fields.Many2One('company.company', 'Company', required=True,
         select=True, domain=[
@@ -1023,85 +922,64 @@
     lines = fields.One2Many('account.tax.rule.line', 'rule', 'Lines')
     template = fields.Many2One('account.tax.rule.template', 'Template')
 
-    def apply(self, rule, tax, pattern):
+    def apply(self, tax, pattern):
         '''
         Apply rule on tax
-
-        :param rule: a rule id or the BrowseRecord of the rule
-        :param tax: a tax id or the BrowseRecord of the tax
-        :param pattern: a dictonary with rule line field as key
-                and match value as value
-        :return: a list of the tax id to use or None
+        pattern is a dictonary with rule line field as key and match value as
+        value.
+        Return a list of the tax id to use or None
         '''
-        tax_obj = Pool().get('account.tax')
-        rule_line_obj = Pool().get('account.tax.rule.line')
-
-        if isinstance(rule, (int, long)) and rule:
-            rule = self.browse(rule)
-
-        if isinstance(tax, (int, long)) and tax:
-            tax = tax_obj.browse(tax)
-
         pattern = pattern.copy()
         pattern['group'] = tax and tax.group.id or None
         pattern['origin_tax'] = tax and tax.id or None
 
-        for line in rule.lines:
-            if rule_line_obj.match(line, pattern):
-                return rule_line_obj.get_taxes(line)
+        for line in self.lines:
+            if line.match(pattern):
+                return line.get_taxes()
         return tax and [tax.id] or None
 
-    def update_rule(self, rule, template2rule=None):
+    def update_rule(self, template2rule=None):
         '''
         Update tax rule based on template.
-
-        :param rule: a rule id or the BrowseRecord of the rule
-        :param template2rule: a dictionary with tax rule template id as key
-                and tax rule id as value, used to convert template id into
-                tax rule. The dictionary is filled with new tax rules
+        template2rule is a dictionary with tax rule template id as key and tax
+        rule id as value, used to convert template id into tax rule. The
+        dictionary is filled with new tax rules.
         '''
-        template_obj = Pool().get('account.tax.rule.template')
-        lang_obj = Pool().get('ir.lang')
+        Lang = Pool().get('ir.lang')
 
         if template2rule is None:
             template2rule = {}
 
-        if isinstance(rule, (int, long)):
-            rule = self.browse(rule)
+        if self.template:
+            vals = self.template._get_tax_rule_value(rule=self)
+            if vals:
+                self.write([self], vals)
 
-        if rule.template:
-            vals = template_obj._get_tax_rule_value(rule.template, rule=rule)
-            if vals:
-                self.write(rule.id, vals)
-
-            prev_lang = rule._context.get('language') or CONFIG['language']
+            prev_lang = self._context.get('language') or CONFIG['language']
             prev_data = {}
-            for field_name, field in rule.template._columns.iteritems():
+            for field_name, field in self.template._fields.iteritems():
                 if getattr(field, 'translate', False):
-                    prev_data[field_name] = rule.template[field_name]
-            for lang in lang_obj.get_translatable_languages():
+                    prev_data[field_name] = getattr(self.template, field_name)
+            for lang in Lang.get_translatable_languages():
                 if lang == prev_lang:
                     continue
-                rule.setLang(lang)
-                data = {}
-                for field_name, field in rule.template._columns.iteritems():
-                    if (getattr(field, 'translate', False)
-                            and rule.template[field_name] !=
-                            prev_data[field_name]):
-                        data[field_name] = rule.template[field_name]
-                if data:
-                    with Transaction().set_context(language=lang):
-                        self.write(rule.id, data)
-            rule.setLang(prev_lang)
+                with Transaction().set_context(language=lang):
+                    rule = self.__class__(self.id)
+                    data = {}
+                    for field_name, field in rule.template._fields.iteritems():
+                        if (getattr(field, 'translate', False)
+                                and (getattr(rule.template, field_name)
+                                    != prev_data[field_name])):
+                            data[field_name] = getattr(rule.template,
+                                field_name)
+                    if data:
+                        self.write([rule], data)
             template2rule[rule.template.id] = rule.id
 
-Rule()
 
-
-class RuleLineTemplate(ModelSQL, ModelView):
+class TaxRuleLineTemplate(ModelSQL, ModelView):
     'Tax Rule Line Template'
-    _name = 'account.tax.rule.line.template'
-    _description = __doc__
+    __name__ = 'account.tax.rule.line.template'
     rule = fields.Many2One('account.tax.rule.template', 'Rule', required=True,
             ondelete='CASCADE')
     group = fields.Many2One('account.tax.group', 'Tax Group')
@@ -1123,90 +1001,75 @@
         order_field='(%(table)s.sequence IS NULL) %(order)s, '
         '%(table)s.sequence %(order)s')
 
-    def __init__(self):
-        super(RuleLineTemplate, self).__init__()
-        self._order.insert(0, ('rule', 'ASC'))
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(TaxRuleLineTemplate, cls).__setup__()
+        cls._order.insert(0, ('rule', 'ASC'))
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(RuleLineTemplate, self).init(module_name)
+        super(TaxRuleLineTemplate, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def _get_tax_rule_line_value(self, template, rule_line=None):
+    def _get_tax_rule_line_value(self, rule_line=None):
         '''
         Set values for tax rule line creation.
-
-        :param template: the BrowseRecord of the template
-        :param rule_line: the BrowseRecord of the rule line to update
-        :return: a dictionary with rule line fields as key and values as value
         '''
         res = {}
-        if not rule_line or rule_line.group.id != template.group.id:
-            res['group'] = template.group.id
-        if not rule_line or rule_line.origin_tax.id != template.origin_tax.id:
-            res['origin_tax'] = template.origin_tax.id
-        if not rule_line or rule_line.sequence != template.sequence:
-            res['sequence'] = template.sequence
-        if not rule_line or rule_line.template.id != template.id:
-            res['template'] = template.id
+        if not rule_line or rule_line.group.id != self.group.id:
+            res['group'] = self.group.id
+        if not rule_line or rule_line.origin_tax.id != self.origin_tax.id:
+            res['origin_tax'] = self.origin_tax.id
+        if not rule_line or rule_line.sequence != self.sequence:
+            res['sequence'] = self.sequence
+        if not rule_line or rule_line.template.id != self.id:
+            res['template'] = self.id
         return res
 
-    def create_rule_line(self, template, template2tax, template2rule,
+    def create_rule_line(self, template2tax, template2rule,
             template2rule_line=None):
         '''
         Create tax rule line based on template.
-
-        :param template: the template id or the BrowseRecord of template
-                used for tax rule line creation
-        :param template2tax: a dictionary with tax template id as key
-                and tax id as value, used to convert template id into
-                tax.
-        :param template2rule: a dictionary with tax rule template id as key
-                and tax rule id as value, used to convert template id into
-                tax rule.
-        :param template2rule_line: a dictionary with tax rule line template id
-                as key and tax rule line id as value, used to convert template
-                id into tax rule line. The dictionary is filled with new
-                tax rule lines
-        :return: id of the tax rule line created
+        template2tax is a dictionary with tax template id as key and tax id as
+        value, used to convert template id into tax.
+        template2rule is a dictionary with tax rule template id as key and tax
+        rule id as value, used to convert template id into tax rule.
+        template2rule_line is a dictionary with tax rule line template id as
+        key and tax rule line id as value, used to convert template id into tax
+        rule line. The dictionary is filled with new tax rule lines.
+        Return id of the tax rule line created
         '''
-        rule_line_obj = Pool().get('account.tax.rule.line')
+        RuleLine = Pool().get('account.tax.rule.line')
 
         if template2rule_line is None:
             template2rule_line = {}
 
-        if isinstance(template, (int, long)):
-            template = self.browse(template)
-
-        if template.id not in template2rule_line:
-            vals = self._get_tax_rule_line_value(template)
-            vals['rule'] = template2rule[template.rule.id]
-            if template.origin_tax:
-                vals['origin_tax'] = template2tax[template.origin_tax.id]
+        if self.id not in template2rule_line:
+            vals = self._get_tax_rule_line_value()
+            vals['rule'] = template2rule[self.rule.id]
+            if self.origin_tax:
+                vals['origin_tax'] = template2tax[self.origin_tax.id]
             else:
                 vals['origin_tax'] = None
-            if template.tax:
-                vals['tax'] = template2tax[template.tax.id]
+            if self.tax:
+                vals['tax'] = template2tax[self.tax.id]
             else:
                 vals['tax'] = None
-            new_id = rule_line_obj.create(vals)
-            template2rule_line[template.id] = new_id
-        else:
-            new_id = template2rule_line[template.id]
-        return new_id
+            new_rule_line = RuleLine.create(vals)
+            template2rule_line[self.id] = new_rule_line.id
+        return template2rule_line[self.id]
 
-RuleLineTemplate()
 
-
-class RuleLine(ModelSQL, ModelView):
+class TaxRuleLine(ModelSQL, ModelView):
     'Tax Rule Line'
-    _name = 'account.tax.rule.line'
-    _description = __doc__
+    __name__ = 'account.tax.rule.line'
+    _rec_name = 'tax'
     rule = fields.Many2One('account.tax.rule', 'Rule', required=True,
             select=True, ondelete='CASCADE')
     group = fields.Many2One('account.tax.group', 'Tax Group')
@@ -1229,132 +1092,111 @@
         '%(table)s.sequence %(order)s')
     template = fields.Many2One('account.tax.rule.line.template', 'Template')
 
-    def __init__(self):
-        super(RuleLine, self).__init__()
-        self._order.insert(0, ('rule', 'ASC'))
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(TaxRuleLine, cls).__setup__()
+        cls._order.insert(0, ('rule', 'ASC'))
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(RuleLine, self).init(module_name)
+        super(TaxRuleLine, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def match(self, line, pattern):
+    def match(self, pattern):
         '''
         Match line on pattern
+        pattern is a dictonary with rule line field as key and match value as
+        value.
+        '''
+        for field in pattern.keys():
+            if field not in self._fields:
+                continue
+            if not getattr(self, field) and field != 'group':
+                continue
+            if self._fields[field]._type == 'many2one':
+                if getattr(self, field).id != pattern[field]:
+                    return False
+            else:
+                if getattr(self, field) != pattern[field]:
+                    return False
+        return True
 
-        :param line: a BrowseRecord of rule line
-        :param pattern: a dictonary with rule line field as key
-                and match value as value
-        :return: a boolean
-        '''
-        res = True
-        for field in pattern.keys():
-            if field not in self._columns:
-                continue
-            if not line[field] and field != 'group':
-                continue
-            if self._columns[field]._type == 'many2one':
-                if line[field].id != pattern[field]:
-                    res = False
-                    break
-            else:
-                if line[field] != pattern[field]:
-                    res = False
-                    break
-        return res
-
-    def get_taxes(self, line):
+    def get_taxes(self):
         '''
         Return list of taxes for a line
-
-        :param line: a BrowseRecord of rule line
-        :return: a list of tax id
         '''
-        if line.tax:
-            return [line.tax.id]
+        if self.tax:
+            return [self.tax.id]
         return None
 
-    def update_rule_line(self, rule_line, template2tax, template2rule,
+    def update_rule_line(self, template2tax, template2rule,
             template2rule_line=None):
         '''
         Update tax rule line based on template.
-
-        :param rule_line: a rule line id or the BrowseRecord of the rule line
-        :param template2tax: a dictionary with tax template id as key
-                and tax id as value, used to convert template id into
-                tax.
-        :param template2rule: a dictionary with tax rule template id as key
-                and tax rule id as value, used to convert template id into
-                tax rule.
-        :param template2rule_line: a dictionary with tax rule line template id
-                as key and tax rule line id as value, used to convert template
-                id into tax rule line. The dictionary is filled with new
-                tax rule lines
+        template2tax is a dictionary with tax template id as key and tax id as
+        value, used to convert template id into tax.
+        template2rule is a dictionary with tax rule template id as key and tax
+        rule id as value, used to convert template id into tax rule.
+        template2rule_line is a dictionary with tax rule line template id as
+        key and tax rule line id as value, used to convert template id into tax
+        rule line. The dictionary is filled with new tax rule lines.
         '''
-        template_obj = Pool().get('account.tax.rule.line.template')
-
         if template2rule_line is None:
             template2rule_line = {}
 
-        if isinstance(rule_line, (int, long)):
-            rule_line = self.browse(rule_line)
+        if self.template:
+            vals = self.template._get_tax_rule_line_value(rule_line=self)
+            if self.rule.id != template2rule[self.template.rule.id]:
+                vals['rule'] = template2rule[self.template.rule.id]
+            if self.origin_tax:
+                if self.template.origin_tax:
+                    if self.origin_tax.id != \
+                            template2tax[self.template.origin_tax.id]:
+                        vals['origin_tax'] = template2tax[
+                            self.template.origin_tax.id]
+            if self.tax:
+                if self.template.tax:
+                    if self.tax.id != template2tax[self.template.tax.id]:
+                        vals['tax'] = template2tax[self.template.tax.id]
+            if vals:
+                self.write([self], vals)
+            template2rule_line[self.template.id] = self.id
 
-        if rule_line.template:
-            vals = template_obj._get_tax_rule_line_value(rule_line.template,
-                    rule_line=rule_line)
-            if rule_line.rule.id != template2rule[rule_line.template.rule.id]:
-                vals['rule'] = template2rule[rule_line.template.rule.id]
-            if rule_line.origin_tax:
-                if rule_line.template.origin_tax:
-                    if rule_line.origin_tax.id != \
-                            template2tax[rule_line.template.origin_tax.id]:
-                        vals['origin_tax'] = template2tax[
-                            rule_line.template.origin_tax.id]
-            if rule_line.tax:
-                if rule_line.template.tax:
-                    if rule_line.tax.id != \
-                            template2tax[rule_line.template.tax.id]:
-                        vals['tax'] = template2tax[rule_line.template.tax.id]
-            if vals:
-                self.write(rule_line.id, vals)
-            template2rule_line[rule_line.template.id] = rule_line.id
 
-RuleLine()
-
-
-class OpenCode(Wizard):
+class OpenTaxCode(Wizard):
     'Open Code'
-    _name = 'account.tax.open_code'
+    __name__ = 'account.tax.open_code'
     start_state = 'open_'
     open_ = StateAction('account.act_tax_line_form')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         pool = Pool()
-        fiscalyear_obj = pool.get('account.fiscalyear')
-        period_obj = pool.get('account.period')
+        FiscalYear = pool.get('account.fiscalyear')
+        Period = pool.get('account.period')
 
         if not Transaction().context.get('fiscalyear'):
-            fiscalyear_ids = fiscalyear_obj.search([
-                ('state', '=', 'open'),
-                ])
+            fiscalyears = FiscalYear.search([
+                    ('state', '=', 'open'),
+                    ])
         else:
-            fiscalyear_ids = [Transaction().context['fiscalyear']]
+            fiscalyears = [FiscalYear(Transaction().context['fiscalyear'])]
 
-        period_ids = []
+        periods = []
         if not Transaction().context.get('periods'):
-            period_ids = period_obj.search([
-                ('fiscalyear', 'in', fiscalyear_ids),
-                ])
+            periods = Period.search([
+                    ('fiscalyear', 'in', [f.id for f in fiscalyears]),
+                    ])
         else:
-            period_ids = Transaction().context['periods']
+            periods = Period.browse(Transaction().context['periods'])
 
         action['pyson_domain'] = PYSONEncoder().encode([
-                ('move_line.period', 'in', period_ids),
+                ('move_line.period', 'in', [p.id for p in periods]),
                 ('code', '=', Transaction().context['active_id']),
                 ])
         if Transaction().context.get('fiscalyear'):
@@ -1363,50 +1205,40 @@
                     })
         else:
             action['pyson_context'] = PYSONEncoder().encode({
-                    'periods': period_ids,
+                    'periods': [p.id for p in periods],
                     })
         return action, {}
 
-OpenCode()
-
 
 class AccountTemplateTaxTemplate(ModelSQL):
     'Account Template - Tax Template'
-    _name = 'account.account.template-account.tax.template'
+    __name__ = 'account.account.template-account.tax.template'
     _table = 'account_account_template_tax_rel'
-    _description = __doc__
     account = fields.Many2One('account.account.template', 'Account Template',
             ondelete='CASCADE', select=True, required=True)
     tax = fields.Many2One('account.tax.template', 'Tax Template',
             ondelete='RESTRICT', select=True, required=True)
 
-AccountTemplateTaxTemplate()
 
-
-class AccountTemplate(ModelSQL, ModelView):
-    _name = 'account.account.template'
+class AccountTemplate2:
+    __name__ = 'account.account.template'
     taxes = fields.Many2Many('account.account.template-account.tax.template',
             'account', 'tax', 'Default Taxes',
             domain=[('parent', '=', None)])
 
-AccountTemplate()
-
 
 class AccountTax(ModelSQL):
     'Account - Tax'
-    _name = 'account.account-account.tax'
+    __name__ = 'account.account-account.tax'
     _table = 'account_account_tax_rel'
-    _description = __doc__
     account = fields.Many2One('account.account', 'Account', ondelete='CASCADE',
             select=True, required=True)
     tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
             select=True, required=True)
 
-AccountTax()
 
-
-class Account(ModelSQL, ModelView):
-    _name = 'account.account'
+class Account2:
+    __name__ = 'account.account'
     taxes = fields.Many2Many('account.account-account.tax',
             'account', 'tax', 'Default Taxes',
             domain=[
@@ -1416,5 +1248,3 @@
             help='Default tax for manual encoding of move lines \n' \
                     'for journal types: "expense" and "revenue"',
             depends=['company'])
-
-Account()
diff -r f40675df5d4c trytond/modules/account/tests/test_account.py
--- a/trytond/modules/account/tests/test_account.py	Fri Sep 21 15:57:18 2012 +0200
+++ b/trytond/modules/account/tests/test_account.py	Mon Sep 24 20:36:38 2012 +0200
@@ -15,7 +15,6 @@
 from trytond.tests.test_tryton import test_view, test_depends
 from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT
 from trytond.transaction import Transaction
-from trytond.wizard import Session
 
 
 class AccountTestCase(unittest.TestCase):
@@ -50,38 +49,33 @@
         'Test creation of minimal chart of accounts'
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            account_template_id, = self.account_template.search([
-                ('parent', '=', False),
-                ])
-            company_id, = self.company.search([('name', '=', 'B2CK')])
-            self.user.write(USER, {
-                    'main_company': company_id,
-                    'company': company_id,
+            account_template, = self.account_template.search([
+                    ('parent', '=', None),
+                    ])
+            company, = self.company.search([('name', '=', 'B2CK')])
+            self.user.write([self.user(USER)], {
+                    'main_company': company.id,
+                    'company': company.id,
                     })
             CONTEXT.update(self.user.get_preferences(context_only=True))
 
             session_id, _, _ = self.account_create_chart.create()
-            session = Session(self.account_create_chart, session_id)
-            session.data['account'].update({
-                    'account_template': account_template_id,
-                    'company': company_id,
-                    })
-            self.account_create_chart.transition_create_account(session)
-            receivable_id, = self.account.search([
+            create_chart = self.account_create_chart(session_id)
+            create_chart.account.account_template = account_template
+            create_chart.account.company = company
+            create_chart.transition_create_account()
+            receivable, = self.account.search([
                     ('kind', '=', 'receivable'),
-                    ('company', '=', company_id),
+                    ('company', '=', company.id),
                     ])
-            payable_id, = self.account.search([
+            payable, = self.account.search([
                     ('kind', '=', 'payable'),
-                    ('company', '=', company_id),
+                    ('company', '=', company.id),
                     ])
-            session.data['properties'].update({
-                    'company': company_id,
-                    'account_receivable': receivable_id,
-                    'account_payable': payable_id,
-                    })
-            self.account_create_chart.transition_create_properties(
-                session)
+            create_chart.properties.company = company
+            create_chart.properties.account_receivable = receivable
+            create_chart.properties.account_payable = payable
+            create_chart.transition_create_properties()
             transaction.cursor.commit()
 
     def test0020fiscalyear(self):
@@ -91,21 +85,20 @@
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             today = datetime.date.today()
-            company_id, = self.company.search([('name', '=', 'B2CK')])
-            sequence_id = self.sequence.create({
+            company, = self.company.search([('name', '=', 'B2CK')])
+            sequence = self.sequence.create({
                     'name': '%s' % today.year,
                     'code': 'account.move',
-                    'company': company_id,
+                    'company': company.id,
                     })
-            fiscalyear_id = self.fiscalyear.create({
+            fiscalyear = self.fiscalyear.create({
                     'name': '%s' % today.year,
                     'start_date': today.replace(month=1, day=1),
                     'end_date': today.replace(month=12, day=31),
-                    'company': company_id,
-                    'post_move_sequence': sequence_id,
+                    'company': company.id,
+                    'post_move_sequence': sequence.id,
                     })
-            self.fiscalyear.create_period([fiscalyear_id])
-            fiscalyear = self.fiscalyear.browse(fiscalyear_id)
+            self.fiscalyear.create_period([fiscalyear])
             self.assertEqual(len(fiscalyear.periods), 12)
             transaction.cursor.commit()
 
diff -r e32ff462bf6e trytond/modules/account_invoice/__init__.py
--- a/trytond/modules/account_invoice/__init__.py	Mon Sep 10 15:27:42 2012 +0200
+++ b/trytond/modules/account_invoice/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,7 +1,37 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .payment_term import *
 from .invoice import *
 from .party import *
 from .account import *
+
+
+def register():
+    Pool.register(
+        PaymentTerm,
+        PaymentTermLine,
+        Invoice,
+        InvoicePaymentLine,
+        InvoiceLine,
+        InvoiceLineTax,
+        InvoiceTax,
+        PrintInvoiceWarning,
+        PayInvoiceStart,
+        PayInvoiceAsk,
+        CreditInvoiceStart,
+        Address,
+        Party,
+        FiscalYear,
+        Period,
+        Reconciliation,
+        module='account_invoice', type_='model')
+    Pool.register(
+        PrintInvoice,
+        PayInvoice,
+        CreditInvoice,
+        module='account_invoice', type_='wizard')
+    Pool.register(
+        InvoiceReport,
+        module='account_invoice', type_='report')
diff -r e32ff462bf6e trytond/modules/account_invoice/account.py
--- a/trytond/modules/account_invoice/account.py	Mon Sep 10 15:27:42 2012 +0200
+++ b/trytond/modules/account_invoice/account.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,12 +1,15 @@
 #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 Model, ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['FiscalYear', 'Period', 'Reconciliation']
+__metaclass__ = PoolMeta
 
-class FiscalYear(ModelSQL, ModelView):
-    _name = 'account.fiscalyear'
+
+class FiscalYear:
+    __name__ = 'account.fiscalyear'
     out_invoice_sequence = fields.Many2One('ir.sequence.strict',
         'Customer Invoice Sequence', required=True,
         domain=[
@@ -62,12 +65,13 @@
             'company': Eval('company'),
             }, depends=['company'])
 
-    def __init__(self):
-        super(FiscalYear, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(FiscalYear, cls).__setup__()
+        cls._constraints += [
             ('check_invoice_sequences', 'different_invoice_sequence'),
-        ]
-        self._error_messages.update({
+            ]
+        cls._error_messages.update({
             'change_invoice_sequence': 'You can not change ' \
                     'the invoice sequence if there is already ' \
                     'an invoice opened in the fiscalyear',
@@ -75,44 +79,41 @@
                     'invoice sequences per fiscal year!',
             })
 
-    def check_invoice_sequences(self, ids):
-        for fiscalyear in self.browse(ids):
-            for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
-                    'out_credit_note_sequence', 'in_credit_note_sequence'):
-                if self.search([
-                    (sequence, '=', fiscalyear[sequence].id),
-                    ('id', '!=', fiscalyear.id),
-                    ]):
-                    return False
+    def check_invoice_sequences(self):
+        for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
+                'out_credit_note_sequence', 'in_credit_note_sequence'):
+            if self.search([
+                        (sequence, '=', getattr(self, sequence).id),
+                        ('id', '!=', self.id),
+                        ]):
+                return False
         return True
 
-    def write(self, ids, vals):
-        invoice_obj = Pool().get('account.invoice')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def write(cls, fiscalyears, vals):
+        Invoice = Pool().get('account.invoice')
 
         for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
                 'out_credit_note_sequence', 'in_credit_note_sequence'):
             if vals.get(sequence):
-                for fiscalyear in self.browse(ids):
-                    if fiscalyear[sequence] and \
-                            fiscalyear[sequence].id != \
-                            vals[sequence]:
-                        if invoice_obj.search([
-                            ('invoice_date', '>=', fiscalyear.start_date),
-                            ('invoice_date', '<=', fiscalyear.end_date),
-                            ('number', '!=', None),
-                            ('type', '=', sequence[:-9]),
-                            ]):
-                            self.raise_user_error('change_invoice_sequence')
-        return super(FiscalYear, self).write(ids, vals)
+                for fiscalyear in fiscalyears:
+                    if (getattr(fiscalyear, sequence)
+                            and (getattr(fiscalyear, sequence).id !=
+                                vals[sequence])):
+                        if Invoice.search([
+                                    ('invoice_date', '>=',
+                                        fiscalyear.start_date),
+                                    ('invoice_date', '<=',
+                                        fiscalyear.end_date),
+                                    ('number', '!=', None),
+                                    ('type', '=', sequence[:-9]),
+                                    ]):
+                            cls.raise_user_error('change_invoice_sequence')
+        super(FiscalYear, cls).write(fiscalyears, vals)
 
-FiscalYear()
 
-
-class Period(ModelSQL, ModelView):
-    _name = 'account.period'
+class Period:
+    __name__ = 'account.period'
     out_invoice_sequence = fields.Many2One('ir.sequence.strict',
         'Customer Invoice Sequence',
         domain=[('code', '=', 'account.invoice')],
@@ -150,12 +151,13 @@
             },
         depends=['type'])
 
-    def __init__(self):
-        super(Period, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Period, cls).__setup__()
+        cls._constraints += [
             ('check_invoice_sequences', 'check_invoice_sequences'),
-        ]
-        self._error_messages.update({
+            ]
+        cls._error_messages.update({
             'change_invoice_sequence': 'You can not change ' \
                     'the invoice sequence if there is already ' \
                     'an invoice opened in the period',
@@ -164,86 +166,75 @@
                     'in the same company!',
             })
 
-    def check_invoice_sequences(self, ids):
-        for period in self.browse(ids):
-            for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
-                    'out_credit_note_sequence', 'in_credit_note_sequence'):
-                if self.search([
-                    (sequence, '=', period[sequence].id),
-                    ('fiscalyear', '!=', period.fiscalyear.id),
-                    ]):
-                    return False
-                if period[sequence].company \
-                        and period[sequence].company.id != \
-                        period.fiscalyear.company.id:
-                    return False
+    def check_invoice_sequences(self):
+        for sequence_name in ('out_invoice_sequence', 'in_invoice_sequence',
+                'out_credit_note_sequence', 'in_credit_note_sequence'):
+            sequence = getattr(self, sequence_name)
+            if self.search([
+                        (sequence_name, '=', sequence.id),
+                        ('fiscalyear', '!=', self.fiscalyear.id),
+                        ]):
+                return False
+            if (sequence.company
+                    and sequence.company != self.fiscalyear.company):
+                return False
         return True
 
-    def create(self, vals):
-        fiscalyear_obj = Pool().get('account.fiscalyear')
+    @classmethod
+    def create(cls, vals):
+        FiscalYear = Pool().get('account.fiscalyear')
         vals = vals.copy()
         if vals.get('fiscalyear'):
-            fiscalyear = fiscalyear_obj.browse(vals['fiscalyear'])
+            fiscalyear = FiscalYear(vals['fiscalyear'])
             for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
                     'out_credit_note_sequence', 'in_credit_note_sequence'):
                 if not vals.get(sequence):
-                    vals[sequence] = fiscalyear[sequence].id
-        return super(Period, self).create(vals)
+                    vals[sequence] = getattr(fiscalyear, sequence).id
+        return super(Period, cls).create(vals)
 
-    def write(self, ids, vals):
-        invoice_obj = Pool().get('account.invoice')
+    @classmethod
+    def write(cls, periods, vals):
+        Invoice = Pool().get('account.invoice')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+        for sequence_name in ('out_invoice_sequence', 'in_invoice_sequence',
+                'out_credit_note_sequence', 'in_credit_note_sequence'):
+            if vals.get(sequence_name):
+                for period in periods:
+                    sequence = getattr(period, sequence_name)
+                    if (sequence and
+                            sequence.id != vals[sequence]):
+                        if Invoice.search([
+                                    ('invoice_date', '>=', period.start_date),
+                                    ('invoice_date', '<=', period.end_date),
+                                    ('number', '!=', None),
+                                    ('type', '=', sequence_name[:-9]),
+                                    ]):
+                            cls.raise_user_error('change_invoice_sequence')
+        super(Period, cls).write(periods, vals)
 
-        for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
-                'out_credit_note_sequence', 'in_credit_note_sequence'):
-            if vals.get(sequence):
-                for period in self.browse(ids):
-                    if period[sequence] and \
-                            period[sequence].id != \
-                            vals[sequence]:
-                        if invoice_obj.search([
-                            ('invoice_date', '>=', period.start_date),
-                            ('invoice_date', '<=', period.end_date),
-                            ('number', '!=', None),
-                            ('type', '=', sequence[:-9]),
-                            ]):
-                            self.raise_user_error('change_invoice_sequence')
-        return super(Period, self).write(ids, vals)
 
-Period()
+class Reconciliation:
+    __name__ = 'account.move.reconciliation'
 
+    @classmethod
+    def create(cls, values):
+        Invoice = Pool().get('account.invoice')
 
-class Reconciliation(Model):
-    _name = 'account.move.reconciliation'
-
-    def create(self, values):
-        pool = Pool()
-        invoice_obj = pool.get('account.invoice')
-
-        result = super(Reconciliation, self).create(values)
-        reconciliation = self.browse(result)
+        reconciliation = super(Reconciliation, cls).create(values)
         move_ids = set(l.move.id for l in reconciliation.lines)
-        invoice_ids = invoice_obj.search([
+        invoices = Invoice.search([
                 ('move', 'in', list(move_ids)),
                 ])
-        invoice_obj.process(invoice_ids)
-        return result
+        Invoice.process(invoices)
+        return reconciliation
 
-    def delete(self, ids):
-        pool = Pool()
-        invoice_obj = pool.get('account.invoice')
+    @classmethod
+    def delete(cls, reconciliations):
+        Invoice = Pool().get('account.invoice')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        reconciliations = self.browse(ids)
         move_ids = set(l.move.id for r in reconciliations for l in r.lines)
-        invoice_ids = invoice_obj.search([
+        invoices = Invoice.search([
                 ('move', 'in', list(move_ids)),
                 ])
-        result = super(Reconciliation, self).delete(ids)
-        invoice_obj.process(invoice_ids)
-        return result
-
-Reconciliation()
+        super(Reconciliation, cls).delete(reconciliations)
+        Invoice.process(invoices)
diff -r e32ff462bf6e trytond/modules/account_invoice/invoice.py
--- a/trytond/modules/account_invoice/invoice.py	Mon Sep 10 15:27:42 2012 +0200
+++ b/trytond/modules/account_invoice/invoice.py	Mon Sep 24 20:36:39 2012 +0200
@@ -13,6 +13,13 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 from trytond.config import CONFIG
+from trytond.rpc import RPC
+
+__all__ = ['Invoice', 'InvoicePaymentLine', 'InvoiceLine',
+    'InvoiceLineTax', 'InvoiceTax',
+    'PrintInvoiceWarning', 'PrintInvoice', 'InvoiceReport',
+    'PayInvoiceStart', 'PayInvoiceAsk', 'PayInvoice',
+    'CreditInvoiceStart', 'CreditInvoice']
 
 _STATES = {
     'readonly': Eval('state') != 'draft',
@@ -38,8 +45,7 @@
 
 class Invoice(Workflow, ModelSQL, ModelView):
     'Invoice'
-    _name = 'account.invoice'
-    _description = __doc__
+    __name__ = 'account.invoice'
     _order_name = 'number'
     company = fields.Many2One('company.company', 'Company', required=True,
         states=_STATES, select=True, domain=[
@@ -79,7 +85,7 @@
         required=True, states=_STATES, depends=_DEPENDS,
         on_change=['party', 'payment_term', 'type', 'company'])
     party_lang = fields.Function(fields.Char('Party Language',
-        on_change_with=['party']), 'get_party_language')
+        on_change_with=['party']), 'on_change_with_party_lang')
     invoice_address = fields.Many2One('party.address', 'Invoice Address',
         required=True, states=_STATES, depends=['state', 'party'],
         domain=[('party', '=', Eval('party'))])
@@ -89,9 +95,9 @@
                 | (Eval('lines') & Eval('currency'))),
             }, depends=['state', 'lines'])
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['currency']), 'get_currency_digits')
+        on_change_with=['currency']), 'on_change_with_currency_digits')
     currency_date = fields.Function(fields.Date('Currency Date',
-        on_change_with=['invoice_date']), 'get_currency_date',)
+        on_change_with=['invoice_date']), 'on_change_with_currency_date',)
     journal = fields.Many2One('account.journal', 'Journal', required=True,
         states=_STATES, depends=_DEPENDS, domain=[('centralised', '=', False)])
     move = fields.Many2One('account.move', 'Move', readonly=True)
@@ -138,17 +144,18 @@
     invoice_report_cache = fields.Binary('Invoice Report', readonly=True)
     invoice_report_format = fields.Char('Invoice Report Format', readonly=True)
 
-    def __init__(self):
-        super(Invoice, self).__init__()
-        self._check_modify_exclude = ['state', 'payment_lines',
+    @classmethod
+    def __setup__(cls):
+        super(Invoice, cls).__setup__()
+        cls._check_modify_exclude = ['state', 'payment_lines',
                 'invoice_report_cache', 'invoice_report_format']
-        self._constraints += [
+        cls._constraints += [
             ('check_account', 'account_different_company'),
             ('check_account2', 'same_account_on_line'),
         ]
-        self._order.insert(0, ('number', 'DESC'))
-        self._order.insert(1, ('id', 'DESC'))
-        self._error_messages.update({
+        cls._order.insert(0, ('number', 'DESC'))
+        cls._order.insert(1, ('id', 'DESC'))
+        cls._error_messages.update({
             'reset_draft': 'You can not reset to draft ' \
                     'an invoice that has move!',
             'missing_tax_line': 'Taxes defined ' \
@@ -178,7 +185,7 @@
                     'as on invoice line!',
             'delete_cancel': 'Invoice "%s" must be cancelled before deletion!',
             })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('draft', 'proforma'),
                 ('proforma', 'open'),
                 ('draft', 'open'),
@@ -189,7 +196,7 @@
                 ('proforma', 'cancel'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': ~Eval('state').in_(['draft', 'proforma']),
                     },
@@ -211,10 +218,11 @@
                     },
                 })
 
-    def init(self, module_name):
-        super(Invoice, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Invoice, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.2 invoice_date is no more required
         table.not_null_action('invoice_date', action='remove')
@@ -226,18 +234,18 @@
                 and table.column_exist('invoice_report_cache')):
             limit = cursor.IN_MAX
             cursor.execute('SELECT COUNT(id) '
-                'FROM "' + self._table + '"')
+                'FROM "' + cls._table + '"')
             invoice_count, = cursor.fetchone()
             for offset in range(0, invoice_count, limit):
                 cursor.execute(cursor.limit_clause(
                     'SELECT id, invoice_report '
-                    'FROM "' + self._table + '"'
+                    'FROM "' + cls._table + '"'
                     'ORDER BY id',
                     limit, offset))
                 for invoice_id, report in cursor.fetchall():
                     if report:
                         report = buffer(base64.decodestring(str(report)))
-                        cursor.execute('UPDATE "' + self._table + '" '
+                        cursor.execute('UPDATE "' + cls._table + '" '
                             'SET invoice_report_cache = %s '
                             'WHERE id = %s', (report, invoice_id))
             table.drop_column('invoice_report')
@@ -245,283 +253,231 @@
         # Add index on create_date
         table.index_action('create_date', action='add')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return Transaction().context.get('type', 'out_invoice')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_currency(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency():
+        Company = Pool().get('company.company')
         if Transaction().context.get('company'):
-            company = company_obj.browse(Transaction().context['company'])
+            company = Company(Transaction().context['company'])
             return company.currency.id
 
-    def default_currency_digits(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency_digits():
+        Company = Pool().get('company.company')
         if Transaction().context.get('company'):
-            company = company_obj.browse(Transaction().context['company'])
+            company = Company(Transaction().context['company'])
             return company.currency.digits
         return 2
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_payment_term(self):
-        payment_term_obj = Pool().get('account.invoice.payment_term')
-        payment_term_ids = payment_term_obj.search(self.payment_term.domain)
-        if len(payment_term_ids) == 1:
-            return payment_term_ids[0]
+    @classmethod
+    def default_payment_term(cls):
+        PaymentTerm = Pool().get('account.invoice.payment_term')
+        payment_terms = PaymentTerm.search(cls.payment_term.domain)
+        if len(payment_terms) == 1:
+            return payment_terms[0].id
 
-    def __get_account_payment_term(self, type_, party_id, company_id):
+    def __get_account_payment_term(self):
         '''
         Return default account and payment term
         '''
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        company_obj = pool.get('company.company')
-        account_obj = pool.get('account.account')
-        payment_term_obj = pool.get('account.invoice.payment_term')
-
+        account = None
+        payment_term = None
         result = {
             'account': None,
             }
-        if party_id:
-            party = party_obj.browse(party_id)
-            if type_ in ('out_invoice', 'out_credit_note'):
-                result['account'] = party.account_receivable.id
-                if type_ == 'out_invoice' and party.customer_payment_term:
-                    result['payment_term'] = party.customer_payment_term.id
-            elif type_ in ('in_invoice', 'in_credit_note'):
-                result['account'] = party.account_payable.id
-                if type_ == 'in_invoice' and party.supplier_payment_term:
-                    result['payment_term'] = party.supplier_payment_term.id
-        if company_id and type_ in ('out_credit_note', 'in_credit_note'):
-            company = company_obj.browse(company_id)
-            if type_ == 'out_credit_note':
-                result['payment_term'] = company.customer_payment_term.id
+        if self.party:
+            if self.type in ('out_invoice', 'out_credit_note'):
+                account = self.party.account_receivable
+                if (self.type == 'out_invoice'
+                        and self.party.customer_payment_term):
+                    payment_term = self.party.customer_payment_term
+            elif self.type in ('in_invoice', 'in_credit_note'):
+                account = self.party.account_payable
+                if (self.type == 'in_invoice'
+                        and self.party.supplier_payment_term):
+                    payment_term = self.party.supplier_payment_term
+        if self.company and self.type in ('out_credit_note', 'in_credit_note'):
+            if self.type == 'out_credit_note':
+                payment_term = self.company.customer_payment_term
             else:
-                result['payment_term'] = company.supplier_payment_term.id
-        if result['account']:
-            result['account.rec_name'] = account_obj.browse(
-                    result['account']).rec_name
-        if result.get('payment_term'):
-            result['payment_term.rec_name'] = payment_term_obj.browse(
-                    result['payment_term']).rec_name
+                payment_term = self.company.supplier_payment_term
+        if account:
+            result['account'] = account.id
+            result['account.rec_name'] = account.rec_name
+        if payment_term:
+            result['payment_term'] = payment_term.id
+            result['payment_term.rec_name'] = payment_term.rec_name
         return result
 
-    def on_change_type(self, vals):
-        journal_obj = Pool().get('account.journal')
+    def on_change_type(self):
+        Journal = Pool().get('account.journal')
         res = {}
-        journal_ids = journal_obj.search([
-            ('type', '=', _TYPE2JOURNAL.get(vals.get('type', 'out_invoice'),
-                'revenue')),
-            ], limit=1)
-        if journal_ids:
-            journal = journal_obj.browse(journal_ids[0])
+        journals = Journal.search([
+                ('type', '=', _TYPE2JOURNAL.get(self.type or 'out_invoice',
+                        'revenue')),
+                ], limit=1)
+        if journals:
+            journal, = journals
             res['journal'] = journal.id
             res['journal.rec_name'] = journal.rec_name
-        res.update(self.__get_account_payment_term(vals.get('type'),
-                vals.get('party'), vals.get('company')))
+        res.update(self.__get_account_payment_term())
         return res
 
-    def on_change_party(self, vals):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        address_obj = pool.get('party.address')
+    def on_change_party(self):
         res = {
             'invoice_address': None,
-        }
-        res.update(self.__get_account_payment_term(vals.get('type'),
-                vals.get('party'), vals.get('company')))
+            }
+        res.update(self.__get_account_payment_term())
 
-        if vals.get('party'):
-            party = party_obj.browse(vals['party'])
-            res['invoice_address'] = party_obj.address_get(party.id,
-                    type='invoice')
-        if res['invoice_address']:
-            res['invoice_address.rec_name'] = address_obj.browse(
-                    res['invoice_address']).rec_name
+        if self.party:
+            invoice_address = self.party.address_get(type='invoice')
+            res['invoice_address'] = invoice_address.id
+            res['invoice_address.rec_name'] = invoice_address.rec_name
         return res
 
-    def on_change_with_currency_digits(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-            return currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.currency:
+            return self.currency.digits
         return 2
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for invoice in self.browse(ids):
-            res[invoice.id] = invoice.currency.digits
-        return res
+    def on_change_with_currency_date(self, name=None):
+        Date = Pool().get('ir.date')
+        return self.invoice_date or Date.today()
 
-    def on_change_with_currency_date(self, vals):
-        date_obj = Pool().get('ir.date')
-        today = date_obj.today()
-        date = vals.get('invoice_date') or today
-        return date
-
-    def get_currency_date(self, ids, name):
-        date_obj = Pool().get('ir.date')
-        res = {}
-        today = date_obj.today()
-        for invoice in self.browse(ids):
-            res[invoice.id] = invoice.invoice_date or today
-        return res
-
-    def on_change_with_party_lang(self, vals):
-        party_obj = Pool().get('party.party')
-        if vals.get('party'):
-            party = party_obj.browse(vals['party'])
-            if party.lang:
-                return party.lang.code
+    def on_change_with_party_lang(self, name=None):
+        if self.party:
+            if self.party.lang:
+                return self.party.lang.code
         return CONFIG['language']
 
-    def get_party_language(self, ids, name):
-        '''
-        Return the language code of the party of each invoice
-
-        :param ids: the ids of the invoices
-        :param name: the field name
-        :return: a dictionary with invoice id as key and
-            language code as value
-        '''
-        res = {}
-        for invoice in self.browse(ids):
-            if invoice.party.lang:
-                res[invoice.id] = invoice.party.lang.code
-            else:
-                res[invoice.id] = CONFIG['language']
-        return res
-
-    def get_type_name(self, ids, name):
-        res = {}
+    @classmethod
+    def get_type_name(self, invoices, name):
+        type_names = {}
         type2name = {}
         for type, name in self.fields_get(fields_names=['type']
                 )['type']['selection']:
             type2name[type] = name
-        for invoice in self.browse(ids):
-            res[invoice.id] = type2name[invoice.type]
-        return res
+        for invoice in invoices:
+            type_names[invoice.id] = type2name[invoice.type]
+        return type_names
 
-    def on_change_lines(self, vals):
-        return self._on_change_lines_taxes(vals)
+    def on_change_lines(self):
+        return self._on_change_lines_taxes()
 
-    def on_change_taxes(self, vals):
-        return self._on_change_lines_taxes(vals)
+    def on_change_taxes(self):
+        return self._on_change_lines_taxes()
 
-    def _on_change_lines_taxes(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        tax_obj = Pool().get('account.tax')
+    def _on_change_lines_taxes(self):
+        pool = Pool()
+        Tax = pool.get('account.tax')
         res = {
             'untaxed_amount': Decimal('0.0'),
             'tax_amount': Decimal('0.0'),
             'total_amount': Decimal('0.0'),
             'taxes': {},
-        }
-        currency = None
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
+            }
         computed_taxes = {}
-        if vals.get('lines'):
-            context = self.get_tax_context(vals)
-            for line in vals['lines']:
-                if line.get('type', 'line') != 'line':
+        if self.lines:
+            context = self.get_tax_context()
+            for line in self.lines:
+                if (line.type or 'line') != 'line':
                     continue
-                res['untaxed_amount'] += line.get('amount') or 0
+                res['untaxed_amount'] += line.amount or 0
                 with Transaction().set_context(**context):
-                    taxes = tax_obj.compute(line.get('taxes', []),
-                            line.get('unit_price') or Decimal('0.0'),
-                            line.get('quantity', 0.0))
+                    taxes = Tax.compute(line.taxes,
+                        line.unit_price or Decimal('0.0'),
+                        line.quantity or 0.0)
                 for tax in taxes:
                     key, val = self._compute_tax(tax,
-                            vals.get('type', 'out_invoice'))
+                        self.type or 'out_invoice')
                     if not key in computed_taxes:
                         computed_taxes[key] = val
                     else:
                         computed_taxes[key]['base'] += val['base']
                         computed_taxes[key]['amount'] += val['amount']
-        if currency:
+        if self.currency:
             for key in computed_taxes:
                 for field in ('base', 'amount'):
-                    computed_taxes[key][field] = currency_obj.round(currency,
-                            computed_taxes[key][field])
+                    computed_taxes[key][field] = self.currency.round(
+                        computed_taxes[key][field])
         tax_keys = []
-        for tax in vals.get('taxes', []):
-            if tax.get('manual', False):
-                res['tax_amount'] += tax.get('amount') or Decimal('0.0')
+        for tax in (self.taxes or []):
+            if tax.manual:
+                res['tax_amount'] += tax.amount or Decimal('0.0')
                 continue
-            key = (tax.get('base_code'), tax.get('base_sign'),
-                    tax.get('tax_code'), tax.get('tax_sign'),
-                    tax.get('account'), tax.get('tax'))
+            key = (tax.base_code, tax.base_sign,
+                tax.tax_code, tax.tax_sign,
+                tax.account, tax.tax)
             if (key not in computed_taxes) or (key in tax_keys):
                 res['taxes'].setdefault('remove', [])
-                res['taxes']['remove'].append(tax.get('id'))
+                res['taxes']['remove'].append(tax.id)
                 continue
             tax_keys.append(key)
-            if currency:
-                if not currency_obj.is_zero(currency,
+            if self.currency:
+                if not self.currency.is_zero(
                         computed_taxes[key]['base'] - \
-                                (tax.get('base') or Decimal('0.0'))):
+                            (tax.base or Decimal('0.0'))):
                     res['tax_amount'] += computed_taxes[key]['amount']
                     res['taxes'].setdefault('update', [])
                     res['taxes']['update'].append({
-                        'id': tax.get('id'),
+                            'id': tax.id,
+                            'amount': computed_taxes[key]['amount'],
+                            'base': computed_taxes[key]['base'],
+                            })
+                else:
+                    res['tax_amount'] += tax.amount or Decimal('0.0')
+            else:
+                if (computed_taxes[key]['base'] - (tax.base or Decimal('0.0'))
+                        != Decimal('0.0')):
+                    res['tax_amount'] += computed_taxes[key]['amount']
+                    res['taxes'].setdefault('update', [])
+                    res['taxes']['update'].append({
+                        'id': tax.id,
                         'amount': computed_taxes[key]['amount'],
                         'base': computed_taxes[key]['base'],
                         })
                 else:
-                    res['tax_amount'] += tax.get('amount') or Decimal('0.0')
-            else:
-                if computed_taxes[key]['base'] - \
-                        (tax.get('base') or Decimal('0.0')) != Decimal('0.0'):
-                    res['tax_amount'] += computed_taxes[key]['amount']
-                    res['taxes'].setdefault('update', [])
-                    res['taxes']['update'].append({
-                        'id': tax.get('id'),
-                        'amount': computed_taxes[key]['amount'],
-                        'base': computed_taxes[key]['base'],
-                        })
-                else:
-                    res['tax_amount'] += tax.get('amount') or Decimal('0.0')
+                    res['tax_amount'] += tax.amount or Decimal('0.0')
         for key in computed_taxes:
             if key not in tax_keys:
                 res['tax_amount'] += computed_taxes[key]['amount']
                 res['taxes'].setdefault('add', [])
-                value = tax_obj.default_get(tax_obj._columns.keys())
+                value = Tax.default_get(Tax._fields.keys())
                 value.update(computed_taxes[key])
                 res['taxes']['add'].append(value)
-        if currency:
-            res['untaxed_amount'] = currency_obj.round(currency,
-                    res['untaxed_amount'])
-            res['tax_amount'] = currency_obj.round(currency, res['tax_amount'])
+        if self.currency:
+            res['untaxed_amount'] = self.currency.round(res['untaxed_amount'])
+            res['tax_amount'] = self.currency.round(res['tax_amount'])
         res['total_amount'] = res['untaxed_amount'] + res['tax_amount']
-        if currency:
-            res['total_amount'] = currency_obj.round(currency,
-                    res['total_amount'])
+        if self.currency:
+            res['total_amount'] = self.currency.round(res['total_amount'])
         return res
 
-    def get_untaxed_amount(self, ids, name):
-        currency_obj = Pool().get('currency.currency')
-        res = {}
-        for invoice in self.browse(ids):
-            res.setdefault(invoice.id, _ZERO)
-            for line in invoice.lines:
-                if line.type != 'line':
-                    continue
-                res[invoice.id] += line.amount
-            res[invoice.id] = currency_obj.round(invoice.currency,
-                    res[invoice.id])
-        return res
+    def get_untaxed_amount(self, name):
+        amount = _ZERO
+        for line in self.lines:
+            if line.type != 'line':
+                continue
+            amount += line.amount
+        return self.currency.round(amount)
 
-    def get_tax_amount(self, ids, name):
-        currency_obj = Pool().get('currency.currency')
+    @classmethod
+    def get_tax_amount(cls, invoices, name):
         cursor = Transaction().cursor
         res = {}
-        type_name = FIELDS[self.tax_amount._type].sql_type(self.tax_amount)[0]
-        red_sql, red_ids = reduce_ids('invoice', ids)
+        type_name = FIELDS[cls.tax_amount._type].sql_type(cls.tax_amount)[0]
+        red_sql, red_ids = reduce_ids('invoice', [i.id for i in invoices])
         cursor.execute('SELECT invoice, ' \
                     'CAST(COALESCE(SUM(amount), 0) AS ' + type_name + ') ' \
                 'FROM account_invoice_tax ' \
@@ -533,66 +489,56 @@
                 sum = Decimal(str(sum))
             res[invoice_id] = sum
 
-        for invoice in self.browse(ids):
+        for invoice in invoices:
             res.setdefault(invoice.id, Decimal('0.0'))
-            res[invoice.id] = currency_obj.round(invoice.currency,
-                    res[invoice.id])
+            res[invoice.id] = invoice.currency.round(res[invoice.id])
         return res
 
-    def get_total_amount(self, ids, name):
-        currency_obj = Pool().get('currency.currency')
-        res = {}
-        for invoice in self.browse(ids):
-            res[invoice.id] = currency_obj.round(invoice.currency,
-                    invoice.untaxed_amount + invoice.tax_amount)
-        return res
+    def get_total_amount(self, name):
+        return self.currency.round(self.untaxed_amount + self.tax_amount)
 
-    def get_reconciled(self, ids, name):
-        res = {}
-        for invoice in self.browse(ids):
-            res[invoice.id] = True
-            if not invoice.lines_to_pay:
-                res[invoice.id] = False
-                continue
-            for line in invoice.lines_to_pay:
-                if not line.reconciliation:
-                    res[invoice.id] = False
-                    break
-        return res
+    def get_reconciled(self, name):
+        if not self.lines_to_pay:
+            return False
+        for line in self.lines_to_pay:
+            if not line.reconciliation:
+                return False
+        return True
 
-    def get_lines_to_pay(self, ids, name):
-        res = {}
-        for invoice in self.browse(ids):
-            lines = []
-            if invoice.move:
-                for line in invoice.move.lines:
-                    if line.account.id == invoice.account.id \
-                            and line.maturity_date:
-                        lines.append(line)
-            lines.sort(key=operator.attrgetter('maturity_date'))
-            res[invoice.id] = [x.id for x in lines]
-        return res
+    def get_lines_to_pay(self, name):
+        lines = []
+        if self.move:
+            for line in self.move.lines:
+                if (line.account.id == self.account.id
+                        and line.maturity_date):
+                    lines.append(line)
+        lines.sort(key=operator.attrgetter('maturity_date'))
+        return [x.id for x in lines]
 
-    def get_amount_to_pay(self, ids, name):
-        currency_obj = Pool().get('currency.currency')
-        date_obj = Pool().get('ir.date')
+    @classmethod
+    def get_amount_to_pay(cls, invoices, name):
+        pool = Pool()
+        Currency = pool.get('currency.currency')
+        Date = pool.get('ir.date')
 
-        compute_ids = self.search([
-            ('id', 'in', ids),
-            ('state', '=', 'open'),
-            ])
+        computes = cls.search([
+                ('id', 'in', [i.id for i in invoices]),
+                ('state', '=', 'open'),
+                ])
 
-        res = dict((x, _ZERO) for x in ids)
-        for invoice in self.browse(compute_ids):
+        today = Date.today()
+        res = dict((x.id, _ZERO) for x in invoices)
+        for invoice in computes:
             amount = _ZERO
             amount_currency = _ZERO
             for line in invoice.lines_to_pay:
                 if line.reconciliation:
                     continue
-                if name == 'amount_to_pay_today' \
-                        and line.maturity_date > date_obj.today():
+                if (name == 'amount_to_pay_today'
+                        and line.maturity_date > today):
                     continue
-                if line.second_currency.id == invoice.currency.id:
+                if (line.second_currency
+                        and line.second_currency == invoice.currency):
                     if line.debit - line.credit > _ZERO:
                         amount_currency += abs(line.amount_second_currency)
                     else:
@@ -602,7 +548,8 @@
             for line in invoice.payment_lines:
                 if line.reconciliation:
                     continue
-                if line.second_currency.id == invoice.currency.id:
+                if (line.second_currency
+                        and line.second_currency == invoice.currency):
                     if line.debit - line.credit > _ZERO:
                         amount_currency += abs(line.amount_second_currency)
                     else:
@@ -614,40 +561,41 @@
                 amount_currency = - amount_currency
             if amount != _ZERO:
                 with Transaction().set_context(date=invoice.currency_date):
-                    amount_currency += currency_obj.compute(
-                    invoice.company.currency.id, amount, invoice.currency.id)
+                    amount_currency += Currency.compute(
+                        invoice.company.currency, amount, invoice.currency)
             if amount_currency < _ZERO:
                 amount_currency = _ZERO
             res[invoice.id] = amount_currency
         return res
 
-    def search_total_amount(self, name, clause):
+    @classmethod
+    def search_total_amount(cls, name, clause):
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
-        line_obj = pool.get('account.invoice.line')
-        tax_obj = pool.get('account.invoice.tax')
-        type_name = FIELDS[self.total_amount._type].sql_type(
-                self.total_amount)[0]
+        Rule = pool.get('ir.rule')
+        Line = pool.get('account.invoice.line')
+        Tax = pool.get('account.invoice.tax')
+        type_name = FIELDS[cls.total_amount._type].sql_type(
+            cls.total_amount)[0]
         cursor = Transaction().cursor
 
-        invoice_query, invoice_val = rule_obj.domain_get('account.invoice')
+        invoice_query, invoice_val = Rule.domain_get('account.invoice')
 
         cursor.execute('SELECT invoice FROM ('
                     'SELECT invoice, '
                         'COALESCE(SUM(quantity * unit_price), 0) '
                             'AS total_amount '
-                    'FROM "' + line_obj._table + '" '
-                    'JOIN "' + self._table + '" ON '
-                        '("' + self._table + '".id = '
-                            '"' + line_obj._table + '".invoice) '
+                    'FROM "' + Line._table + '" '
+                    'JOIN "' + cls._table + '" ON '
+                        '("' + cls._table + '".id = '
+                            '"' + Line._table + '".invoice) '
                     'WHERE ' + invoice_query + ' '
                     'GROUP BY invoice '
                 'UNION '
                     'SELECT invoice, COALESCE(SUM(amount), 0) AS total_amount '
-                    'FROM "' + tax_obj._table + '" '
-                    'JOIN "' + self._table + '" ON '
-                        '("' + self._table + '".id = '
-                            '"' + tax_obj._table + '".invoice) '
+                    'FROM "' + Tax._table + '" '
+                    'JOIN "' + cls._table + '" ON '
+                        '("' + cls._table + '".id = '
+                            '"' + Tax._table + '".invoice) '
                     'WHERE ' + invoice_query + ' '
                     'GROUP BY invoice  '
                 ') AS u '
@@ -657,20 +605,21 @@
                 invoice_val + invoice_val + [str(clause[2])])
         return [('id', 'in', [x[0] for x in cursor.fetchall()])]
 
-    def search_untaxed_amount(self, name, clause):
+    @classmethod
+    def search_untaxed_amount(cls, name, clause):
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
-        line_obj = pool.get('account.invoice.line')
-        type_name = FIELDS[self.untaxed_amount._type].sql_type(
-                self.untaxed_amount)[0]
+        Rule = pool.get('ir.rule')
+        Line = pool.get('account.invoice.line')
+        type_name = FIELDS[cls.untaxed_amount._type].sql_type(
+                cls.untaxed_amount)[0]
         cursor = Transaction().cursor
 
-        invoice_query, invoice_val = rule_obj.domain_get('account.invoice')
+        invoice_query, invoice_val = Rule.domain_get('account.invoice')
 
-        cursor.execute('SELECT invoice FROM "' + line_obj._table + '" '
-                'JOIN "' + self._table + '" ON '
-                    '("' + self._table + '".id = '
-                        '"' + line_obj._table + '".invoice) '
+        cursor.execute('SELECT invoice FROM "' + Line._table + '" '
+                'JOIN "' + cls._table + '" ON '
+                    '("' + cls._table + '".id = '
+                        '"' + Line._table + '".invoice) '
                 'WHERE ' + invoice_query + ' '
                 'GROUP BY invoice '
                 'HAVING (CAST(COALESCE(SUM(quantity * unit_price), 0) '
@@ -678,20 +627,21 @@
                 invoice_val + [str(clause[2])])
         return [('id', 'in', [x[0] for x in cursor.fetchall()])]
 
-    def search_tax_amount(self, name, clause):
+    @classmethod
+    def search_tax_amount(cls, name, clause):
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
-        tax_obj = pool.get('account.invoice.tax')
-        type_name = FIELDS[self.tax_amount._type].sql_type(
-                self.tax_amount)[0]
+        Rule = pool.get('ir.rule')
+        Tax = pool.get('account.invoice.tax')
+        type_name = FIELDS[cls.tax_amount._type].sql_type(
+                cls.tax_amount)[0]
         cursor = Transaction().cursor
 
-        invoice_query, invoice_val = rule_obj.domain_get('account.invoice')
+        invoice_query, invoice_val = Rule.domain_get('account.invoice')
 
-        cursor.execute('SELECT invoice FROM "' + tax_obj._table + '" '
-                'JOIN "' + self._table + '" ON '
-                    '("' + self._table + '".id = '
-                        '"' + tax_obj._table + '".invoice) '
+        cursor.execute('SELECT invoice FROM "' + Tax._table + '" '
+                'JOIN "' + cls._table + '" ON '
+                    '("' + cls._table + '".id = '
+                        '"' + Tax._table + '".invoice) '
                 'WHERE ' + invoice_query + ' '
                 'GROUP BY invoice '
                 'HAVING (CAST(COALESCE(SUM(amount), 0) '
@@ -699,62 +649,61 @@
                 invoice_val + [str(clause[2])])
         return [('id', 'in', [x[0] for x in cursor.fetchall()])]
 
-    def get_tax_context(self, invoice):
-        party_obj = Pool().get('party.party')
-        res = {}
-        if isinstance(invoice, dict):
-            if invoice.get('party'):
-                party = party_obj.browse(invoice['party'])
-                if party.lang:
-                    res['language'] = party.lang.code
-        else:
-            if invoice.party.lang:
-                res['language'] = invoice.party.lang.code
-        return res
+    def get_tax_context(self):
+        context = {}
+        if self.party and self.party.lang:
+            context['language'] = self.party.lang.code
+        return context
 
-    def _compute_tax(self, tax, invoice_type):
+    @staticmethod
+    def _compute_tax(invoice_tax, invoice_type):
         val = {}
+        tax = invoice_tax['tax']
         val['manual'] = False
-        val['description'] = tax['tax'].description
-        val['base'] = tax['base']
-        val['amount'] = tax['amount']
-        val['tax'] = tax['tax'].id
+        val['description'] = tax.description
+        val['base'] = invoice_tax['base']
+        val['amount'] = invoice_tax['amount']
+        val['tax'] = tax.id if tax else None
 
         if invoice_type in ('out_invoice', 'in_invoice'):
-            val['base_code'] = tax['tax'].invoice_base_code.id
-            val['base_sign'] = tax['tax'].invoice_base_sign
-            val['tax_code'] = tax['tax'].invoice_tax_code.id
-            val['tax_sign'] = tax['tax'].invoice_tax_sign
-            val['account'] = tax['tax'].invoice_account.id
+            val['base_code'] = (tax.invoice_base_code.id
+                if tax.invoice_base_code else None)
+            val['base_sign'] = tax.invoice_base_sign
+            val['tax_code'] = (tax.invoice_tax_code.id
+                if tax.invoice_tax_code else None)
+            val['tax_sign'] = tax.invoice_tax_sign
+            val['account'] = (tax.invoice_account.id
+                if tax.invoice_account else None)
         else:
-            val['base_code'] = tax['tax'].credit_note_base_code.id
-            val['base_sign'] = tax['tax'].credit_note_base_sign
-            val['tax_code'] = tax['tax'].credit_note_tax_code.id
-            val['tax_sign'] = tax['tax'].credit_note_tax_sign
-            val['account'] = tax['tax'].credit_note_account.id
+            val['base_code'] = (tax.credit_note_base_code.id
+                if tax.credit_note_base_code else None)
+            val['base_sign'] = tax.credit_note_base_sign
+            val['tax_code'] = (tax.credit_note_tax_code.id
+                if tax.credit_note_tax_code else None)
+            val['tax_sign'] = tax.credit_note_tax_sign
+            val['account'] = (tax.credit_note_account.id
+                if tax.credit_note_account else None)
         key = (val['base_code'], val['base_sign'],
                 val['tax_code'], val['tax_sign'],
                 val['account'], val['tax'])
         return key, val
 
-    def _compute_taxes(self, invoice):
-        tax_obj = Pool().get('account.tax')
-        currency_obj = Pool().get('currency.currency')
+    def _compute_taxes(self):
+        Tax = Pool().get('account.tax')
 
-        context = self.get_tax_context(invoice)
+        context = self.get_tax_context()
 
         res = {}
-        for line in invoice.lines:
+        for line in self.lines:
             # Don't round on each line to handle rounding error
             if line.type != 'line':
                 continue
-            tax_ids = [x.id for x in line.taxes]
             with Transaction().set_context(**context):
-                taxes = tax_obj.compute(tax_ids, line.unit_price,
+                taxes = Tax.compute(line.taxes, line.unit_price,
                         line.quantity)
             for tax in taxes:
-                key, val = self._compute_tax(tax, invoice.type)
-                val['invoice'] = invoice.id
+                key, val = self._compute_tax(tax, self.type)
+                val['invoice'] = self.id
                 if not key in res:
                     res[key] = val
                 else:
@@ -762,82 +711,81 @@
                     res[key]['amount'] += val['amount']
         for key in res:
             for field in ('base', 'amount'):
-                res[key][field] = currency_obj.round(invoice.currency,
-                        res[key][field])
+                res[key][field] = self.currency.round(res[key][field])
         return res
 
-    def update_taxes(self, ids, exception=False):
-        tax_obj = Pool().get('account.invoice.tax')
-        currency_obj = Pool().get('currency.currency')
-        for invoice in self.browse(ids):
+    @classmethod
+    def update_taxes(cls, invoices, exception=False):
+        Tax = Pool().get('account.invoice.tax')
+        for invoice in invoices:
             if invoice.state in ('open', 'paid', 'cancel'):
                 continue
-            computed_taxes = self._compute_taxes(invoice)
+            computed_taxes = invoice._compute_taxes()
             if not invoice.taxes:
                 for tax in computed_taxes.values():
-                    tax_obj.create(tax)
+                    Tax.create(tax)
             else:
                 tax_keys = []
                 for tax in invoice.taxes:
                     if tax.manual:
                         continue
-                    key = (tax.base_code.id, tax.base_sign,
-                            tax.tax_code.id, tax.tax_sign,
-                            tax.account.id, tax.tax.id)
+                    base_code_id = tax.base_code.id if tax.base_code else None
+                    tax_code_id = tax.tax_code.id if tax.tax_code else None
+                    tax_id = tax.tax.id if tax.tax else None
+                    key = (base_code_id, tax.base_sign,
+                            tax_code_id, tax.tax_sign,
+                            tax.account.id, tax_id)
                     if (not key in computed_taxes) or (key in tax_keys):
                         if exception:
-                            self.raise_user_error('missing_tax_line')
-                        tax_obj.delete(tax.id)
+                            cls.raise_user_error('missing_tax_line')
+                        Tax.delete([tax])
                         continue
                     tax_keys.append(key)
-                    if not currency_obj.is_zero(invoice.currency,
+                    if not invoice.currency.is_zero(
                             computed_taxes[key]['base'] - tax.base):
                         if exception:
-                            self.raise_user_error('diff_tax_line')
-                        tax_obj.write(tax.id, computed_taxes[key])
+                            cls.raise_user_error('diff_tax_line')
+                        Tax.write([tax], computed_taxes[key])
                 for key in computed_taxes:
                     if not key in tax_keys:
                         if exception:
-                            self.raise_user_error('missing_tax_line')
-                        tax_obj.create(computed_taxes[key])
-        return True
+                            cls.raise_user_error('missing_tax_line')
+                        Tax.create(computed_taxes[key])
 
-    def _get_move_line_invoice_line(self, invoice):
+    def _get_move_line_invoice_line(self):
         '''
         Return list of move line values for each invoice lines
         '''
-        line_obj = Pool().get('account.invoice.line')
         res = []
-        for line in invoice.lines:
-            val = line_obj.get_move_line(line)
+        for line in self.lines:
+            val = line.get_move_line()
             if val:
                 res.extend(val)
         return res
 
-    def _get_move_line_invoice_tax(self, invoice):
+    def _get_move_line_invoice_tax(self):
         '''
         Return list of move line values for each invoice taxes
         '''
-        tax_obj = Pool().get('account.invoice.tax')
         res = []
-        for tax in invoice.taxes:
-            val = tax_obj.get_move_line(tax)
+        for tax in self.taxes:
+            val = tax.get_move_line()
             if val:
                 res.extend(val)
         return res
 
-    def _get_move_line(self, invoice, date, amount):
+    def _get_move_line(self, date, amount):
         '''
         Return move line
         '''
-        currency_obj = Pool().get('currency.currency')
+        Currency = Pool().get('currency.currency')
         res = {}
-        if invoice.currency.id != invoice.company.currency.id:
-            with Transaction().set_context(date=invoice.currency_date):
-                res['amount_second_currency'] = currency_obj.compute(
-                    invoice.company.currency.id, amount, invoice.currency.id)
+        if self.currency.id != self.company.currency.id:
+            with Transaction().set_context(date=self.currency_date):
+                res['amount_second_currency'] = Currency.compute(
+                    self.company.currency.id, amount, self.currency.id)
             res['amount_second_currency'] = abs(res['amount_second_currency'])
-            res['second_currency'] = invoice.currency.id
+            res['second_currency'] = self.currency.id
         else:
             res['amount_second_currency'] = Decimal('0.0')
             res['second_currency'] = None
@@ -847,29 +795,27 @@
         else:
             res['debit'] = - amount
             res['credit'] = Decimal('0.0')
-        res['account'] = invoice.account.id
+        res['account'] = self.account.id
         res['maturity_date'] = date
-        res['reference'] = invoice.reference
-        res['name'] = invoice.number
-        res['party'] = invoice.party.id
+        res['reference'] = self.reference
+        res['name'] = self.number
+        res['party'] = self.party.id
         return res
 
-    def create_move(self, invoice):
+    def create_move(self):
         '''
-        Create account move for the invoice and return the created move id
+        Create account move for the invoice and return the created move
         '''
         pool = Pool()
-        payment_term_obj = pool.get('account.invoice.payment_term')
-        currency_obj = pool.get('currency.currency')
-        move_obj = pool.get('account.move')
-        period_obj = pool.get('account.period')
-        date_obj = pool.get('ir.date')
+        Move = pool.get('account.move')
+        Period = pool.get('account.period')
+        Date = pool.get('ir.date')
 
-        if invoice.move:
+        if self.move:
             return True
-        self.update_taxes([invoice.id], exception=True)
-        move_lines = self._get_move_line_invoice_line(invoice)
-        move_lines += self._get_move_line_invoice_tax(invoice)
+        self.update_taxes([self], exception=True)
+        move_lines = self._get_move_line_invoice_line()
+        move_lines += self._get_move_line_invoice_tax()
 
         total = Decimal('0.0')
         total_currency = Decimal('0.0')
@@ -877,130 +823,116 @@
             total += line['debit'] - line['credit']
             total_currency += line['amount_second_currency']
 
-        term_lines = payment_term_obj.compute(total, invoice.company.currency,
-                invoice.payment_term, invoice.invoice_date)
+        term_lines = self.payment_term.compute(total, self.company.currency,
+            self.invoice_date)
         remainder_total_currency = total_currency
         if not term_lines:
-            term_lines = [(date_obj.today(), total)]
+            term_lines = [(Date.today(), total)]
         for date, amount in term_lines:
-            val = self._get_move_line(invoice, date, amount)
+            val = self._get_move_line(date, amount)
             remainder_total_currency -= val['amount_second_currency']
             move_lines.append(val)
-        if not currency_obj.is_zero(invoice.currency,
-                remainder_total_currency):
+        if not self.currency.is_zero(remainder_total_currency):
             move_lines[-1]['amount_second_currency'] += \
                 remainder_total_currency
 
-        accounting_date = invoice.accounting_date or invoice.invoice_date
-        period_id = period_obj.find(invoice.company.id, date=accounting_date)
+        accounting_date = self.accounting_date or self.invoice_date
+        period_id = Period.find(self.company.id, date=accounting_date)
 
-        move_id = move_obj.create({
-            'journal': invoice.journal.id,
+        move = Move.create({
+            'journal': self.journal.id,
             'period': period_id,
             'date': accounting_date,
             'lines': [('create', x) for x in move_lines],
             })
-        self.write(invoice.id, {
-            'move': move_id,
-            })
-        move_obj.post(move_id)
-        return move_id
+        self.write([self], {
+                'move': move.id,
+                })
+        Move.post([move])
+        return move
 
-    def set_number(self, invoice):
+    def set_number(self):
         '''
         Set number to the invoice
         '''
         pool = Pool()
-        period_obj = pool.get('account.period')
-        sequence_obj = pool.get('ir.sequence.strict')
-        date_obj = pool.get('ir.date')
+        Period = pool.get('account.period')
+        Sequence = pool.get('ir.sequence.strict')
+        Date = pool.get('ir.date')
 
-        if invoice.number:
+        if self.number:
             return
 
         test_state = True
-        if invoice.type in ('in_invoice', 'in_credit_note'):
+        if self.type in ('in_invoice', 'in_credit_note'):
             test_state = False
 
-        period_id = period_obj.find(invoice.company.id,
-                date=invoice.invoice_date, test_state=test_state)
-        period = period_obj.browse(period_id)
-        sequence_id = period[invoice.type + '_sequence'].id
+        period_id = Period.find(self.company.id,
+            date=self.invoice_date, test_state=test_state)
+        period = Period(period_id)
+        sequence_id = getattr(period, self.type + '_sequence').id
         if not sequence_id:
             self.raise_user_error('no_invoice_sequence')
         with Transaction().set_context(
-                date=invoice.invoice_date or date_obj.today()):
-            number = sequence_obj.get_id(sequence_id)
+                date=self.invoice_date or Date.today()):
+            number = Sequence.get_id(sequence_id)
             vals = {'number': number}
-            if not invoice.invoice_date:
+            if not self.invoice_date:
                 vals['invoice_date'] = Transaction().context['date']
-        self.write(invoice.id, vals)
+        self.write([self], vals)
 
-    def check_modify(self, ids):
+    @classmethod
+    def check_modify(cls, invoices):
         '''
         Check if the invoices can be modified
         '''
-        for invoice in self.browse(ids):
+        for invoice in invoices:
             if invoice.state in ('open', 'paid'):
-                self.raise_user_error('modify_invoice')
-        return
+                cls.raise_user_error('modify_invoice')
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for invoice in self.browse(ids):
-            res[invoice.id] = invoice.number or unicode(invoice.id) + \
-                    (invoice.reference and (' ' + invoice.reference) or '') + \
-                    ' ' + invoice.party.rec_name
-        return res
+    def get_rec_name(self, name):
+        return (self.number or unicode(self.id)
+            + (self.reference and (' ' + self.reference) or '')
+            + ' ' + self.party.rec_name)
 
-    def search_rec_name(self, name, clause):
-        ids = self.search(['OR',
-            ('number',) + clause[1:],
-            ('reference',) + clause[1:],
-            ], order=[])
-        if ids:
-            return [('id', 'in', ids)]
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        invoices = cls.search(['OR',
+                ('number',) + clause[1:],
+                ('reference',) + clause[1:],
+                ], order=[])
+        if invoices:
+            return [('id', 'in', [i.id for i in invoices])]
         return [('party',) + clause[1:]]
 
-    def delete(self, ids):
-        if not ids:
-            return True
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        self.check_modify(ids)
+    @classmethod
+    def delete(cls, invoices):
+        cls.check_modify(invoices)
         # Cancel before delete
-        self.cancel(ids)
-        for invoice in self.browse(ids):
+        cls.cancel(invoices)
+        for invoice in invoices:
             if invoice.state != 'cancel':
-                self.raise_user_error('delete_cancel', invoice.rec_name)
-        return super(Invoice, self).delete(ids)
+                cls.raise_user_error('delete_cancel', invoice.rec_name)
+        super(Invoice, cls).delete(invoices)
 
-    def write(self, ids, vals):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def write(cls, invoices, vals):
         keys = vals.keys()
-        for key in self._check_modify_exclude:
+        for key in cls._check_modify_exclude:
             if key in keys:
                 keys.remove(key)
         if len(keys):
-            self.check_modify(ids)
-        update_tax_ids = [x.id for x in self.browse(ids) if x.state == 'draft']
-        res = super(Invoice, self).write(ids, vals)
-        if update_tax_ids:
-            self.update_taxes(update_tax_ids)
-        return res
+            cls.check_modify(invoices)
+        update_tax = [i for i in invoices if i.state == 'draft']
+        super(Invoice, cls).write(invoices, vals)
+        if update_tax:
+            cls.update_taxes(update_tax)
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, invoices, default=None):
         pool = Pool()
-        line_obj = pool.get('account.invoice.line')
-        tax_obj = pool.get('account.invoice.tax')
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+        Line = pool.get('account.invoice.line')
+        Tax = pool.get('account.invoice.tax')
 
         if default is None:
             default = {}
@@ -1017,326 +949,295 @@
         default.setdefault('accounting_date', None)
         default['lines_to_pay'] = None
 
-        new_ids = []
-        for invoice in self.browse(ids):
-            new_id = super(Invoice, self).copy(invoice.id, default=default)
-            line_obj.copy([x.id for x in invoice.lines], default={
-                'invoice': new_id,
-                })
-            tax_obj.copy([x.id for x in invoice.taxes], default={
-                'invoice': new_id,
-                })
-            new_ids.append(new_id)
+        new_invoices = []
+        for invoice in invoices:
+            new_invoice, = super(Invoice, cls).copy([invoice], default=default)
+            Line.copy(invoice.lines, default={
+                    'invoice': new_invoice.id,
+                    })
+            Tax.copy(invoice.taxes, default={
+                    'invoice': new_invoice.id,
+                    })
+            new_invoices.append(new_invoice)
+        return new_invoices
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
+    def check_account(self):
+        return self.account.company == self.company
 
-    def check_account(self, ids):
-        for invoice in self.browse(ids):
-            if invoice.account.company.id != invoice.company.id:
+    def check_account2(self):
+        for line in self.lines:
+            if (line.type == 'line'
+                    and line.account == self.account):
                 return False
         return True
 
-    def check_account2(self, ids):
-        for invoice in self.browse(ids):
-            for line in invoice.lines:
-                if line.type == 'line' \
-                        and line.account.id == invoice.account.id:
-                    return False
-        return True
-
-    def get_reconcile_lines_for_amount(self, invoice, amount,
-            exclude_ids=None):
+    def get_reconcile_lines_for_amount(self, amount, exclude_lines=None):
         '''
-        Return list of line ids and the remainder to make reconciliation.
+        Return list of lines and the remainder to make reconciliation.
         '''
-        currency_obj = Pool().get('currency.currency')
-
-        if exclude_ids is None:
-            exclude_ids = []
+        if exclude_lines is None:
+            exclude_lines = []
         payment_amount = Decimal('0.0')
-        remainder = invoice.total_amount
+        remainder = self.total_amount
         lines = []
         payment_lines = []
 
-        for line in invoice.payment_lines:
+        for line in self.payment_lines:
             if line.reconciliation:
                 continue
             payment_amount += line.debit - line.credit
-            payment_lines.append(line.id)
+            payment_lines.append(line)
 
-        if invoice.type in ('out_invoice', 'in_credit_note'):
+        if self.type in ('out_invoice', 'in_credit_note'):
             amount = - abs(amount)
         else:
             amount = abs(amount)
 
-        for line in invoice.lines_to_pay:
+        for line in self.lines_to_pay:
 
             if line.reconciliation:
                 continue
-            if line.id in exclude_ids:
+            if line in exclude_lines:
                 continue
 
             test_amount = amount + (line.debit - line.credit)
-            if currency_obj.is_zero(invoice.currency, test_amount):
-                return ([line.id], Decimal('0.0'))
+            if self.currency.is_zero(test_amount):
+                return ([line], Decimal('0.0'))
             if abs(test_amount) < abs(remainder):
-                lines = [line.id]
+                lines = [line]
                 remainder = test_amount
 
             test_amount = (amount + payment_amount)
             test_amount += (line.debit - line.credit)
-            if currency_obj.is_zero(invoice.currency, test_amount):
-                return ([line.id] + payment_lines, Decimal('0.0'))
+            if self.currency.is_zero(test_amount):
+                return ([line] + payment_lines, Decimal('0.0'))
             if abs(test_amount) < abs(remainder):
-                lines = [line.id] + payment_lines
+                lines = [line] + payment_lines
                 remainder = test_amount
 
-            exclude_ids2 = exclude_ids[:]
-            exclude_ids2.append(line.id)
-            res = self.get_reconcile_lines_for_amount(invoice,
-                    (amount + (line.debit - line.credit)),
-                    exclude_ids=exclude_ids2)
-            if res[1] == Decimal('0.0'):
-                res[0].append(line.id)
-                return res
-            if abs(res[1]) < abs(remainder):
-                res[0].append(line.id)
-                lines = res[0]
-                remainder = res[1]
+            exclude_lines2 = exclude_lines[:]
+            exclude_lines2.append(line)
+            lines2, remainder2 = self.get_reconcile_lines_for_amount(
+                (amount + (line.debit - line.credit)),
+                exclude_lines=exclude_lines2)
+            if remainder2 == Decimal('0.0'):
+                lines2.append(line)
+                return lines2, remainder2
+            if abs(remainder2) < abs(remainder):
+                lines2.append(line)
+                lines, remainder = lines2, remainder2
 
         return (lines, remainder)
 
-    def pay_invoice(self, invoice_id, amount, journal_id, date, description,
+    def pay_invoice(self, amount, journal, date, description,
             amount_second_currency=None, second_currency=None):
         '''
-        Add a payment to an invoice
-
-        :param invoice_id: the invoice id
-        :param amount: the amount to pay
-        :param journal_id: the journal id for the move
-        :param date: the date of the move
-        :param description: the description of the move
-        :param amount_second_currency: the amount in the second currenry if one
-        :param second_currency: the id of the second currency
-        :return: the id of the payment line
+        Adds a payment of amount to an invoice using the journal, date and
+        description.
+        Returns the payment line.
         '''
         pool = Pool()
-        journal_obj = pool.get('account.journal')
-        move_obj = pool.get('account.move')
-        period_obj = pool.get('account.period')
+        Move = pool.get('account.move')
+        Period = pool.get('account.period')
 
         lines = []
-        invoice = self.browse(invoice_id)
-        journal = journal_obj.browse(journal_id)
 
-        if invoice.type in ('out_invoice', 'in_credit_note'):
+        if self.type in ('out_invoice', 'in_credit_note'):
             lines.append({
-                'name': description,
-                'account': invoice.account.id,
-                'party': invoice.party.id,
-                'debit': Decimal('0.0'),
-                'credit': amount,
-                'amount_second_currency': amount_second_currency,
-                'second_currency': second_currency,
-            })
+                    'name': description,
+                    'account': self.account.id,
+                    'party': self.party.id,
+                    'debit': Decimal('0.0'),
+                    'credit': amount,
+                    'amount_second_currency': amount_second_currency,
+                    'second_currency': second_currency,
+                    })
             lines.append({
-                'name': description,
-                'account': journal.debit_account.id,
-                'party': invoice.party.id,
-                'debit': amount,
-                'credit': Decimal('0.0'),
-                'amount_second_currency': amount_second_currency,
-                'second_currency': second_currency,
-            })
-            if invoice.account.id == journal.debit_account.id:
+                    'name': description,
+                    'account': journal.debit_account.id,
+                    'party': self.party.id,
+                    'debit': amount,
+                    'credit': Decimal('0.0'),
+                    'amount_second_currency': amount_second_currency,
+                    'second_currency': second_currency,
+                    })
+            if self.account == journal.debit_account:
                 self.raise_user_error('same_debit_account')
             if not journal.debit_account:
                 self.raise_user_error('missing_debit_account')
         else:
             lines.append({
-                'name': description,
-                'account': invoice.account.id,
-                'party': invoice.party.id,
-                'debit': amount,
-                'credit': Decimal('0.0'),
-                'amount_second_currency': amount_second_currency,
-                'second_currency': second_currency,
-            })
+                    'name': description,
+                    'account': self.account.id,
+                    'party': self.party.id,
+                    'debit': amount,
+                    'credit': Decimal('0.0'),
+                    'amount_second_currency': amount_second_currency,
+                    'second_currency': second_currency,
+                    })
             lines.append({
-                'name': description,
-                'account': journal.credit_account.id,
-                'party': invoice.party.id,
-                'debit': Decimal('0.0'),
-                'credit': amount,
-                'amount_second_currency': amount_second_currency,
-                'second_currency': second_currency,
-            })
-            if invoice.account.id == journal.credit_account.id:
+                    'name': description,
+                    'account': journal.credit_account.id,
+                    'party': self.party.id,
+                    'debit': Decimal('0.0'),
+                    'credit': amount,
+                    'amount_second_currency': amount_second_currency,
+                    'second_currency': second_currency,
+                    })
+            if self.account == journal.credit_account:
                 self.raise_user_error('same_credit_account')
             if not journal.credit_account:
                 self.raise_user_error('missing_credit_account')
 
-        period_id = period_obj.find(invoice.company.id, date=date)
+        period_id = Period.find(self.company.id, date=date)
 
-        move_id = move_obj.create({
-            'journal': journal.id,
-            'period': period_id,
-            'date': date,
-            'lines': [('create', x) for x in lines],
-            })
-        move_obj.post(move_id)
-
-        move = move_obj.browse(move_id)
+        move = Move.create({
+                'journal': journal.id,
+                'period': period_id,
+                'date': date,
+                'lines': [('create', x) for x in lines],
+                })
+        Move.post([move])
 
         for line in move.lines:
-            if line.account.id == invoice.account.id:
-                self.write(invoice.id, {
-                    'payment_lines': [('add', line.id)],
-                    })
-                return line.id
+            if line.account == self.account:
+                self.write([self], {
+                        'payment_lines': [('add', [line.id])],
+                        })
+                return line
         raise Exception('Missing account')
 
-    def print_invoice(self, invoice):
+    def print_invoice(self):
         '''
         Generate invoice report and store it in invoice_report field.
         '''
-        if invoice.invoice_report_cache:
+        if self.invoice_report_cache:
             return
-        invoice_report = Pool().get('account.invoice', type='report')
-        invoice_report.execute([invoice.id], {})
+        InvoiceReport = Pool().get('account.invoice', type='report')
+        InvoiceReport.execute([self.id], {})
 
-    def _credit(self, invoice):
+    def _credit(self):
         '''
         Return values to credit invoice.
         '''
-        invoice_line_obj = Pool().get('account.invoice.line')
-        invoice_tax_obj = Pool().get('account.invoice.tax')
-
         res = {}
-        if invoice.type == 'out_invoice':
+        if self.type == 'out_invoice':
             res['type'] = 'out_credit_note'
-        elif invoice.type == 'in_invoice':
+        elif self.type == 'in_invoice':
             res['type'] = 'in_credit_note'
-        elif invoice.type == 'out_credit_note':
+        elif self.type == 'out_credit_note':
             res['type'] = 'out_invoice'
-        elif invoice.type == 'in_credit_note':
+        elif self.type == 'in_credit_note':
             res['type'] = 'in_invoice'
 
         for field in ('description', 'comment'):
-            res[field] = invoice[field]
+            res[field] = getattr(self, field)
 
-        res['reference'] = invoice.number or invoice.reference
+        res['reference'] = self.number or self.reference
 
         for field in ('company', 'party', 'invoice_address', 'currency',
                 'journal', 'account', 'payment_term'):
-            res[field] = invoice[field].id
+            res[field] = getattr(self, field).id
 
         res['lines'] = []
-        for line in invoice.lines:
-            value = invoice_line_obj._credit(line)
+        for line in self.lines:
+            value = line._credit()
             res['lines'].append(('create', value))
 
         res['taxes'] = []
-        for tax in invoice.taxes:
+        for tax in self.taxes:
             if not tax.manual:
                 continue
-            value = invoice_tax_obj._credit(tax)
+            value = tax._credit()
             res['taxes'].append(('create', value))
         return res
 
-    def credit(self, ids, refund=False):
+    @classmethod
+    def credit(cls, invoices, refund=False):
         '''
         Credit invoices and return ids of new invoices.
+        Return the list of new invoice
+        '''
+        MoveLine = Pool().get('account.move.line')
 
-        :param ids: a list of invoice id
-        :param refund: a boolean to specify the refund
-        :return: the list of new invoice id
-        '''
-        move_line_obj = Pool().get('account.move.line')
+        new_invoices = []
+        for invoice in invoices:
+            new_invoice = cls.create(invoice._credit())
+            new_invoices.append(new_invoice)
+            if refund:
+                cls.open([new_invoice])
+                if new_invoice.state == 'open':
+                    MoveLine.reconcile([l for l in invoice.lines_to_pay
+                            if not l.reconciliation] +
+                        [l for l in new_invoice.lines_to_pay
+                            if not l.reconciliation])
+        return new_invoices
 
-        new_ids = []
-        for invoice in self.browse(ids):
-            vals = self._credit(invoice)
-            new_id = self.create(vals)
-            new_ids.append(new_id)
-            if refund:
-                self.open([new_id])
-                new_invoice = self.browse(new_id)
-                if new_invoice.state == 'open':
-                    line_ids = [x.id for x in invoice.lines_to_pay
-                            if not x.reconciliation] + \
-                                    [x.id for x in new_invoice.lines_to_pay
-                                            if not x.reconciliation]
-                    move_line_obj.reconcile(line_ids)
-        return new_ids
-
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
+    def draft(cls, invoices):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('proforma')
-    def proforma(self, ids):
+    def proforma(cls, invoices):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('open')
-    def open(self, ids):
-        for invoice in self.browse(ids):
-            self.set_number(invoice)
-            self.create_move(invoice)
-            self.print_invoice(invoice)
+    def open(cls, invoices):
+        for invoice in invoices:
+            invoice.set_number()
+            invoice.create_move()
+            invoice.print_invoice()
 
+    @classmethod
     @ModelView.button_action('account_invoice.wizard_pay')
-    def pay(self, ids):
+    def pay(cls, invoices):
         pass
 
-    def process(self, ids):
+    @classmethod
+    def process(cls, invoices):
         paid = []
         open_ = []
-        for invoice in self.browse(ids):
+        for invoice in invoices:
             if invoice.state not in ('open', 'paid'):
                 continue
             if invoice.reconciled:
-                paid.append(invoice.id)
+                paid.append(invoice)
             else:
-                open_.append(invoice.id)
-        self.paid(paid)
-        self.open(open_)
+                open_.append(invoice)
+        cls.paid(paid)
+        cls.open(open_)
 
+    @classmethod
     @Workflow.transition('paid')
-    def paid(self, ids):
+    def paid(cls, invoices):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
+    def cancel(cls, invoices):
         pass
 
-Invoice()
-
 
 class InvoicePaymentLine(ModelSQL):
     'Invoice - Payment Line'
-    _name = 'account.invoice-account.move.line'
-    _description = __doc__
+    __name__ = 'account.invoice-account.move.line'
     invoice = fields.Many2One('account.invoice', 'Invoice', ondelete='CASCADE',
             select=True, required=True)
     line = fields.Many2One('account.move.line', 'Payment Line',
             ondelete='CASCADE', select=True, required=True)
 
-InvoicePaymentLine()
-
 
 class InvoiceLine(ModelSQL, ModelView):
     'Invoice Line'
-    _name = 'account.invoice.line'
+    __name__ = 'account.invoice.line'
     _rec_name = 'description'
-    _description = __doc__
-
     invoice = fields.Many2One('account.invoice', 'Invoice', ondelete='CASCADE',
         select=True, states={
             'required': (~Eval('invoice_type') & Eval('party')
@@ -1356,14 +1257,14 @@
             },
         depends=['invoice'])
     party_lang = fields.Function(fields.Char('Party Language',
-        on_change_with=['party']), 'get_party_language')
+        on_change_with=['party']), 'on_change_with_party_lang')
     currency = fields.Many2One('currency.currency', 'Currency',
         states={
             'required': ~Eval('invoice'),
             },
         depends=['invoice'])
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['currency']), 'get_currency_digits')
+        on_change_with=['currency']), 'on_change_with_currency_digits')
     company = fields.Many2One('company.company', 'Company',
         states={
             'required': ~Eval('invoice'),
@@ -1406,7 +1307,7 @@
             ],
         depends=['product', 'type', 'product_uom_category'])
     unit_digits = fields.Function(fields.Integer('Unit Digits',
-        on_change_with=['unit']), 'get_unit_digits')
+        on_change_with=['unit']), 'on_change_with_unit_digits')
     product = fields.Many2One('product.product', 'Product',
         states={
             'invisible': Eval('type') != 'line',
@@ -1419,7 +1320,7 @@
     product_uom_category = fields.Function(
         fields.Many2One('product.uom.category', 'Product Uom Category',
             on_change_with=['product']),
-        'get_product_uom_category')
+        'on_change_with_product_uom_category')
     account = fields.Many2One('account.account', 'Account',
         domain=[
             ('company', '=', Eval('_parent_invoice', {}).get('company',
@@ -1462,9 +1363,10 @@
     invoice_taxes = fields.Function(fields.One2Many('account.invoice.tax',
         None, 'Invoice Taxes'), 'get_invoice_taxes')
 
-    def __init__(self):
-        super(InvoiceLine, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(InvoiceLine, cls).__setup__()
+        cls._sql_constraints += [
             ('type_account',
                 'CHECK((type = \'line\' AND account IS NOT NULL) ' \
                         'OR (type != \'line\'))',
@@ -1473,13 +1375,13 @@
                 'CHECK((type != \'line\' AND invoice IS NOT NULL) ' \
                         'OR (type = \'line\'))',
                 'Line without "line" type must have an invoice!'),
-        ]
-        self._constraints += [
+            ]
+        cls._constraints += [
             ('check_account', 'account_different_company'),
             ('check_account2', 'same_account_on_invoice'),
-        ]
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._error_messages.update({
+            ]
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._error_messages.update({
             'modify': 'You can not modify line from an invoice ' \
                     'that is opened, paid!',
             'create': 'You can not add a line to an invoice ' \
@@ -1490,10 +1392,11 @@
                     'as on the invoice!',
             })
 
-    def init(self, module_name):
-        super(InvoiceLine, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(InvoiceLine, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 invoice is no more required
         table.not_null_action('invoice', action='remove')
@@ -1501,431 +1404,351 @@
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_invoice_type(self):
+    @staticmethod
+    def default_invoice_type():
         return Transaction().context.get('invoice_type', 'out_invoice')
 
-    def default_currency(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency():
+        Company = Pool().get('company.company')
         if Transaction().context.get('company'):
-            company = company_obj.browse(Transaction().context['company'])
+            company = Company(Transaction().context['company'])
             return company.currency.id
 
-    def default_currency_digits(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency_digits():
+        Company = Pool().get('company.company')
         if Transaction().context.get('company'):
-            company = company_obj.browse(Transaction().context['company'])
+            company = Company(Transaction().context['company'])
             return company.currency.digits
         return 2
 
-    def default_unit_digits(self):
+    @staticmethod
+    def default_unit_digits():
         return 2
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'line'
 
-    def on_change_with_party_lang(self, vals):
-        party_obj = Pool().get('party.party')
-        if vals.get('party'):
-            party = party_obj.browse(vals['party'])
-            if party.lang:
-                return party.lang.code
+    def on_change_with_party_lang(self, name=None):
+        if self.party and self.party.lang:
+            return self.party.lang.code
         return CONFIG['language']
 
-    def get_party_language(self, ids, name):
-        '''
-        Return the language code of the party of each line
+    def on_change_with_unit_digits(self, name=None):
+        if self.unit:
+            return self.unit.digits
+        return 2
 
-        :param ids: the ids of the account.invoice.line
-        :param name: the field name
-        :return: a dictionary with account.invoice.line id as key and
-            language code as value
-        '''
-        res = {}
-        for line in self.browse(ids):
-            if line.party and line.party.lang:
-                res[line.id] = line.party.lang.code
-            else:
-                res[line.id] = CONFIG['language']
-        return res
+    def on_change_with_currency_digits(self, name=None):
+        if self.currency:
+            return self.currency.digits
+        return 2
 
-    def on_change_with_amount(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('type') == 'line':
-            currency = (vals.get('_parent_invoice.currency')
-                or vals.get('currency'))
-            if isinstance(currency, (int, long)) and currency:
-                currency = currency_obj.browse(currency)
-            amount = Decimal(str(vals.get('quantity') or '0.0')) * \
-                    (vals.get('unit_price') or Decimal('0.0'))
+    def on_change_with_amount(self):
+        if self.type == 'line':
+            currency = (self.invoice.currency if self.invoice
+                else self.currency)
+            amount = (Decimal(str(self.quantity or '0.0'))
+                * (self.unit_price or Decimal('0.0')))
             if currency:
-                return currency_obj.round(currency, amount)
+                return currency.round(amount)
             return amount
         return Decimal('0.0')
 
-    def on_change_with_unit_digits(self, vals):
-        uom_obj = Pool().get('product.uom')
-        if vals.get('unit'):
-            uom = uom_obj.browse(vals['unit'])
-            return uom.digits
-        return 2
+    def get_amount(self, name):
+        if self.type == 'line':
+            return self.on_change_with_amount()
+        elif self.type == 'subtotal':
+            subtotal = _ZERO
+            for line2 in self.invoice.lines:
+                if line2.type == 'line':
+                    subtotal += line2.invoice.currency.round(
+                        Decimal(str(line2.quantity)) * line2.unit_price)
+                elif line2.type == 'subtotal':
+                    if self == line2:
+                        break
+                    subtotal = _ZERO
+            return subtotal
+        else:
+            return _ZERO
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            if line.unit:
-                res[line.id] = line.unit.digits
-            else:
-                res[line.id] = 2
-        return res
+    def get_invoice_taxes(self, name):
+        pool = Pool()
+        Tax = pool.get('account.tax')
+        Invoice = pool.get('account.invoice')
 
-    def on_change_with_currency_digits(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-            return currency.digits
-        return 2
+        if not self.invoice:
+            return
+        context = self.invoice.get_tax_context()
+        taxes_keys = []
+        with Transaction().set_context(**context):
+            taxes = Tax.compute(self.taxes, self.unit_price, self.quantity)
+        for tax in taxes:
+            key, _ = Invoice._compute_tax(tax, self.invoice.type)
+            taxes_keys.append(key)
+        taxes = []
+        for tax in self.invoice.taxes:
+            if tax.manual:
+                continue
+            base_code_id = tax.base_code.id if tax.base_code else None
+            tax_code_id = tax.tax_code.id if tax.tax_code else None
+            tax_id = tax.tax.id if tax.tax else None
+            key = (base_code_id, tax.base_sign,
+                    tax_code_id, tax.tax_sign,
+                    tax.account.id, tax_id)
+            if key in taxes_keys:
+                taxes.append(tax.id)
+        return taxes
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            res[line.id] = line.currency and line.currency.digits or 2
-        return res
-
-    def get_amount(self, ids, name):
-        currency_obj = Pool().get('currency.currency')
-        res = {}
-        for line in self.browse(ids):
-            if line.type == 'line':
-                currency = line.invoice and line.invoice.currency \
-                        or line.currency
-                res[line.id] = currency_obj.round(currency,
-                        Decimal(str(line.quantity)) * line.unit_price)
-            elif line.type == 'subtotal':
-                res[line.id] = _ZERO
-                for line2 in line.invoice.lines:
-                    if line2.type == 'line':
-                        res[line.id] += currency_obj.round(
-                            line2.invoice.currency,
-                            Decimal(str(line2.quantity)) * line2.unit_price)
-                    elif line2.type == 'subtotal':
-                        if line.id == line2.id:
-                            break
-                        res[line.id] = _ZERO
-            else:
-                res[line.id] = _ZERO
-        return res
-
-    def get_invoice_taxes(self, ids, name):
-        tax_obj = Pool().get('account.tax')
-        invoice_obj = Pool().get('account.invoice')
-
-        res = {}
-        for line in self.browse(ids):
-            if not line.invoice:
-                res[line.id] = None
-                continue
-            context = invoice_obj.get_tax_context(line.invoice)
-            tax_ids = [x.id for x in line.taxes]
-            taxes_keys = []
-            with Transaction().set_context(**context):
-                taxes = tax_obj.compute(tax_ids, line.unit_price,
-                        line.quantity)
-            for tax in taxes:
-                key, _ = invoice_obj._compute_tax(tax, line.invoice.type)
-                taxes_keys.append(key)
-            res[line.id] = []
-            for tax in line.invoice.taxes:
-                if tax.manual:
-                    continue
-                key = (tax.base_code.id, tax.base_sign,
-                        tax.tax_code.id, tax.tax_sign,
-                        tax.account.id, tax.tax.id)
-                if key in taxes_keys:
-                    res[line.id].append(tax.id)
-        return res
-
-    def _get_tax_rule_pattern(self, party, vals):
+    def _get_tax_rule_pattern(self):
         '''
         Get tax rule pattern
+        '''
+        return {}
 
-        :param party: the BrowseRecord of the party
-        :param vals: a dictionary with value from on_change
-        :return: a dictionary to use as pattern for tax rule
-        '''
-        res = {}
-        return res
+    def on_change_product(self):
+        pool = Pool()
+        Product = pool.get('product.product')
+        Company = pool.get('company.company')
+        Currency = pool.get('currency.currency')
+        Date = pool.get('ir.date')
 
-    def on_change_product(self, vals):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        party_obj = pool.get('party.party')
-        company_obj = pool.get('company.company')
-        currency_obj = pool.get('currency.currency')
-        tax_rule_obj = pool.get('account.tax.rule')
-        invoice_obj = pool.get('account.invoice')
-        date_obj = pool.get('ir.date')
-
-        if not vals.get('product'):
+        if not self.product:
             return {}
         res = {}
 
         context = {}
         party = None
-        if vals.get('_parent_invoice.party') or vals.get('party'):
-            party = party_obj.browse(vals.get('_parent_invoice.party')
-                    or vals.get('party'))
-            if party.lang:
-                context['language'] = party.lang.code
-
-        product = product_obj.browse(vals['product'])
+        if self.invoice and self.invoice.party:
+            party = self.invoice.party
+        elif self.party:
+            party = self.party
+        if party and party.lang:
+            context['language'] = party.lang.code
 
         company = None
         if Transaction().context.get('company'):
-            company = company_obj.browse(Transaction().context['company'])
+            company = Company(Transaction().context['company'])
         currency = None
-        currency_date = date_obj.today()
-        if '_parent_invoice.currency_date' in vals:
-            if vals['_parent_invoice.currency_date']:
-                currency_date = vals['_parent_invoice.currency_date']
-        elif vals.get('invoice'):
-            invoice = invoice_obj.browse(vals['invoice'])
-            currency_date = invoice.currency_date
-        if vals.get('_parent_invoice.currency') or vals.get('currency'):
-            #TODO check if today date is correct
-            currency = currency_obj.browse(
-                    vals.get('_parent_invoice.currency') or
-                    vals.get('currency'))
+        currency_date = Date.today()
+        if self.invoice and self.invoice.currency_date:
+            currency_date = self.invoice.currency_date
+        #TODO check if today date is correct
+        if self.invoice and self.invoice.currency:
+            currency = self.invoice.currency
+        elif self.currency:
+            currency = self.currency
 
-        if (vals.get('_parent_invoice.type') or vals.get('invoice_type')) \
-                in ('in_invoice', 'in_credit_note'):
+        if self.invoice and self.invoice.type:
+            type_ = self.invoice.type
+        elif self.invoice_type:
+            type_ = self.invoice_type
+        if type_ in ('in_invoice', 'in_credit_note'):
             if company and currency:
                 with Transaction().set_context(date=currency_date):
-                    res['unit_price'] = currency_obj.compute(
-                            company.currency.id, product.cost_price,
-                            currency.id, round=False)
+                    res['unit_price'] = Currency.compute(
+                        company.currency, self.product.cost_price,
+                        currency, round=False)
             else:
-                res['unit_price'] = product.cost_price
+                res['unit_price'] = self.product.cost_price
             try:
-                res['account'] = product.account_expense_used.id
-                res['account.rec_name'] = product.account_expense_used.rec_name
+                account_expense = self.product.account_expense_used
+                res['account'] = account_expense.id
+                res['account.rec_name'] = account_expense.rec_name
             except Exception:
                 pass
             res['taxes'] = []
-            pattern = self._get_tax_rule_pattern(party, vals)
-            for tax in product.supplier_taxes_used:
+            pattern = self._get_tax_rule_pattern()
+            for tax in self.product.supplier_taxes_used:
                 if party and party.supplier_tax_rule:
-                    tax_ids = tax_rule_obj.apply(party.supplier_tax_rule, tax,
-                            pattern)
+                    tax_ids = party.supplier_tax_rule.apply(tax, pattern)
                     if tax_ids:
                         res['taxes'].extend(tax_ids)
                     continue
                 res['taxes'].append(tax.id)
             if party and party.supplier_tax_rule:
-                tax_ids = tax_rule_obj.apply(party.supplier_tax_rule, None,
-                        pattern)
+                tax_ids = party.supplier_tax_rule.apply(None, pattern)
                 if tax_ids:
                     res['taxes'].extend(tax_ids)
         else:
             if company and currency:
                 with Transaction().set_context(date=currency_date):
-                    res['unit_price'] = currency_obj.compute(
-                            company.currency.id, product.list_price,
-                            currency.id, round=False)
+                    res['unit_price'] = Currency.compute(
+                        company.currency, self.product.list_price,
+                        currency, round=False)
             else:
-                res['unit_price'] = product.list_price
+                res['unit_price'] = self.product.list_price
             try:
-                res['account'] = product.account_revenue_used.id
-                res['account.rec_name'] = product.account_revenue_used.rec_name
+                account_revenue = self.product.account_revenue_used
+                res['account'] = account_revenue.id
+                res['account.rec_name'] = account_revenue.rec_name
             except Exception:
                 pass
             res['taxes'] = []
-            pattern = self._get_tax_rule_pattern(party, vals)
-            for tax in product.customer_taxes_used:
+            pattern = self._get_tax_rule_pattern()
+            for tax in self.product.customer_taxes_used:
                 if party and party.customer_tax_rule:
-                    tax_ids = tax_rule_obj.apply(party.customer_tax_rule, tax,
-                            pattern)
+                    tax_ids = party.customer_tax_rule.apply(tax, pattern)
                     if tax_ids:
                         res['taxes'].extend(tax_ids)
                     continue
                 res['taxes'].append(tax.id)
             if party and party.customer_tax_rule:
-                tax_ids = tax_rule_obj.apply(party.customer_tax_rule, None,
-                        pattern)
+                tax_ids = party.customer_tax_rule.apply(None, pattern)
                 if tax_ids:
                     res['taxes'].extend(tax_ids)
 
-        if not vals.get('description'):
+        if not self.description:
             with Transaction().set_context(**context):
-                res['description'] = product_obj.browse(product.id).rec_name
+                res['description'] = Product(self.product.id).rec_name
 
-        category = product.default_uom.category
-        if not vals.get('unit') \
-                or vals.get('unit') not in [x.id for x in category.uoms]:
-            res['unit'] = product.default_uom.id
-            res['unit.rec_name'] = product.default_uom.rec_name
-            res['unit_digits'] = product.default_uom.digits
+        category = self.product.default_uom.category
+        if not self.unit or self.unit not in category.uoms:
+            res['unit'] = self.product.default_uom.id
+            res['unit.rec_name'] = self.product.default_uom.rec_name
+            res['unit_digits'] = self.product.default_uom.digits
 
-        vals = vals.copy()
-        vals['unit_price'] = res['unit_price']
-        vals['type'] = 'line'
-        res['amount'] = self.on_change_with_amount(vals)
+        self.unit_price = res['unit_price']
+        self.type = 'line'
+        res['amount'] = self.on_change_with_amount()
         return res
 
-    def on_change_with_product_uom_category(self, values):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        if values.get('product'):
-            product = product_obj.browse(values['product'])
-            return product.default_uom_category.id
+    def on_change_with_product_uom_category(self, name=None):
+        if self.product:
+            return self.product.default_uom_category.id
 
-    def get_product_uom_category(self, ids, name):
-        categories = {}
-        for line in self.browse(ids):
-            if line.product:
-                categories[line.id] = line.product.default_uom_category.id
-            else:
-                categories[line.id] = None
-        return categories
-
-    def on_change_account(self, values):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        account_obj = pool.get('account.account')
-        tax_rule_obj = pool.get('account.tax.rule')
-
-        if values.get('product'):
+    def on_change_account(self):
+        if self.product:
             return {}
         taxes = []
         result = {
             'taxes': taxes,
-        }
-        if (values.get('_parent_invoice.party')
-                and values.get('_parent_invoice.type')):
-            party = party_obj.browse(values['_parent_invoice.party'])
-            if values['_parent_invoice.type'] in ('in_invoice',
-                    'in_credit_note'):
+            }
+        if (self.invoice and self.invoice.party
+                and self.invoice.type):
+            party = self.invoice.party
+            if self.invoice.type in ('in_invoice', 'in_credit_note'):
                 tax_rule = party.supplier_tax_rule
             else:
                 tax_rule = party.customer_tax_rule
         else:
             tax_rule = None
 
-        if values.get('account'):
-            account = account_obj.browse(values['account'])
-            pattern = self._get_tax_rule_pattern(party, values)
-            for tax in account.taxes:
+        if self.account:
+            pattern = self._get_tax_rule_pattern()
+            for tax in self.account.taxes:
                 if tax_rule:
-                    tax_ids = tax_rule_obj.apply(tax_rule, tax, pattern)
+                    tax_ids = tax_rule.apply(tax, pattern)
                     if tax_ids:
                         taxes.extend(tax_ids)
                     continue
                 taxes.append(tax.id)
         return result
 
-    def check_modify(self, ids):
+    @classmethod
+    def check_modify(cls, lines):
         '''
         Check if the lines can be modified
         '''
-        for line in self.browse(ids):
-            if line.invoice and \
-                    line.invoice.state in ('open', 'paid'):
-                self.raise_user_error('modify')
-        return
+        for line in lines:
+            if (line.invoice
+                    and line.invoice.state in ('open', 'paid')):
+                cls.raise_user_error('modify')
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        self.check_modify(ids)
-        return super(InvoiceLine, self).delete(ids)
+    @classmethod
+    def delete(cls, lines):
+        cls.check_modify(lines)
+        super(InvoiceLine, cls).delete(lines)
 
-    def write(self, ids, vals):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        self.check_modify(ids)
-        return super(InvoiceLine, self).write(ids, vals)
+    @classmethod
+    def write(cls, lines, vals):
+        cls.check_modify(lines)
+        super(InvoiceLine, cls).write(lines, vals)
 
-    def create(self, vals):
-        invoice_obj = Pool().get('account.invoice')
+    @classmethod
+    def create(cls, vals):
+        Invoice = Pool().get('account.invoice')
         if vals.get('invoice'):
-            invoice = invoice_obj.browse(vals['invoice'])
+            invoice = Invoice(vals['invoice'])
             if invoice.state in ('open', 'paid', 'cancel'):
-                self.raise_user_error('create')
-        return super(InvoiceLine, self).create(vals)
+                cls.raise_user_error('create')
+        return super(InvoiceLine, cls).create(vals)
 
-    def check_account(self, ids):
-        for line in self.browse(ids):
-            if line.type == 'line':
-                if line.invoice:
-                    if line.account.company.id != line.invoice.company.id:
-                        return False
-                elif line.company:
-                    if line.account.company.id != line.company.id:
-                        return False
-        return True
-
-    def check_account2(self, ids):
-        for line in self.browse(ids):
-            if line.type == 'line':
-                if line.invoice \
-                        and line.account.id == line.invoice.account.id:
+    def check_account(self):
+        if self.type == 'line':
+            if self.invoice:
+                if self.account.company != self.invoice.company:
+                    return False
+            elif self.company:
+                if self.account.company != self.company:
                     return False
         return True
 
-    def _compute_taxes(self, line):
+    def check_account2(self):
+        if self.type == 'line':
+            if (self.invoice
+                    and self.account == self.invoice.account):
+                return False
+        return True
+
+    def _compute_taxes(self):
         pool = Pool()
-        tax_obj = pool.get('account.tax')
-        currency_obj = pool.get('currency.currency')
-        invoice_obj = pool.get('account.invoice')
+        Tax = pool.get('account.tax')
+        Currency = pool.get('currency.currency')
 
-        context = invoice_obj.get_tax_context(line.invoice)
+        context = self.invoice.get_tax_context()
         res = []
-        if line.type != 'line':
+        if self.type != 'line':
             return res
-        tax_ids = [x.id for x in line.taxes]
         with Transaction().set_context(**context):
-            taxes = tax_obj.compute(tax_ids, line.unit_price, line.quantity)
+            taxes = Tax.compute(self.taxes, self.unit_price, self.quantity)
         for tax in taxes:
-            if line.invoice.type in ('out_invoice', 'in_invoice'):
-                base_code_id = tax['tax'].invoice_base_code.id
+            if self.invoice.type in ('out_invoice', 'in_invoice'):
+                base_code_id = (tax['tax'].invoice_base_code.id
+                    if tax['tax'].invoice_base_code else None)
                 amount = tax['base'] * tax['tax'].invoice_base_sign
             else:
-                base_code_id = tax['tax'].credit_note_base_code.id
+                base_code_id = (tax['tax'].credit_note_base_code.id
+                    if tax['tax'].credit_note_base_code else None)
                 amount = tax['base'] * tax['tax'].credit_note_base_sign
             if base_code_id:
                 with Transaction().set_context(
-                        date=line.invoice.currency_date):
-                    amount = currency_obj.compute(line.invoice.currency.id,
-                            amount, line.invoice.company.currency.id)
+                        date=self.invoice.currency_date):
+                    amount = Currency.compute(self.invoice.currency,
+                        amount, self.invoice.company.currency)
                 res.append({
-                    'code': base_code_id,
-                    'amount': amount,
-                    'tax': tax['tax'].id
-                })
+                        'code': base_code_id,
+                        'amount': amount,
+                        'tax': tax['tax'].id if tax['tax'] else None,
+                        })
         return res
 
-    def get_move_line(self, line):
+    def get_move_line(self):
         '''
         Return a list of move lines values for invoice line
         '''
-        currency_obj = Pool().get('currency.currency')
+        Currency = Pool().get('currency.currency')
         res = {}
-        if line.type != 'line':
+        if self.type != 'line':
             return []
-        res['name'] = line.description
-        if line.invoice.currency.id != line.invoice.company.currency.id:
-            with Transaction().set_context(date=line.invoice.currency_date):
-                amount = currency_obj.compute(line.invoice.currency.id,
-                        line.amount, line.invoice.company.currency.id)
-            res['amount_second_currency'] = line.amount
-            res['second_currency'] = line.invoice.currency.id
+        res['name'] = self.description
+        if self.invoice.currency != self.invoice.company.currency:
+            with Transaction().set_context(date=self.invoice.currency_date):
+                amount = Currency.compute(self.invoice.currency,
+                    self.amount, self.invoice.company.currency)
+            res['amount_second_currency'] = self.amount
+            res['second_currency'] = self.invoice.currency.id
         else:
-            amount = line.amount
+            amount = self.amount
             res['amount_second_currency'] = Decimal('0.0')
             res['second_currency'] = None
-        if line.invoice.type in ('in_invoice', 'out_credit_note'):
+        if self.invoice.type in ('in_invoice', 'out_credit_note'):
             if amount >= Decimal('0.0'):
                 res['debit'] = amount
                 res['credit'] = Decimal('0.0')
@@ -1941,15 +1764,15 @@
             else:
                 res['debit'] = - amount
                 res['credit'] = Decimal('0.0')
-        res['account'] = line.account.id
-        res['party'] = line.invoice.party.id
-        computed_taxes = self._compute_taxes(line)
+        res['account'] = self.account.id
+        res['party'] = self.invoice.party.id
+        computed_taxes = self._compute_taxes()
         for tax in computed_taxes:
             res.setdefault('tax_lines', [])
             res['tax_lines'].append(('create', tax))
         return [res]
 
-    def _credit(self, line):
+    def _credit(self):
         '''
         Return values to credit line.
         '''
@@ -1957,38 +1780,31 @@
 
         for field in ('sequence', 'type', 'quantity', 'unit_price',
                 'description'):
-            res[field] = line[field]
+            res[field] = getattr(self, field)
 
         for field in ('unit', 'product', 'account'):
-            res[field] = line[field].id
+            res[field] = getattr(self, field).id
 
         res['taxes'] = []
-        for tax in line.taxes:
+        for tax in self.taxes:
             res['taxes'].append(('add', tax.id))
         return res
 
-InvoiceLine()
-
 
 class InvoiceLineTax(ModelSQL):
     'Invoice Line - Tax'
-    _name = 'account.invoice.line-account.tax'
+    __name__ = 'account.invoice.line-account.tax'
     _table = 'account_invoice_line_account_tax'
-    _description = __doc__
     line = fields.Many2One('account.invoice.line', 'Invoice Line',
             ondelete='CASCADE', select=True, required=True)
     tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
             required=True)
 
-InvoiceLineTax()
-
 
 class InvoiceTax(ModelSQL, ModelView):
     'Invoice Tax'
-    _name = 'account.invoice.tax'
+    __name__ = 'account.invoice.tax'
     _rec_name = 'description'
-    _description = __doc__
-
     invoice = fields.Many2One('account.invoice', 'Invoice', ondelete='CASCADE',
             select=True)
     description = fields.Char('Description', size=None, required=True)
@@ -2027,59 +1843,67 @@
             '_parent_invoice.type'],
         depends=['manual'])
 
-    def __init__(self):
-        super(InvoiceTax, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(InvoiceTax, cls).__setup__()
+        cls._constraints += [
             ('check_company', 'You can not create invoice tax \n' \
                     'with account or code from a different invoice company!',
                     ['account', 'base_code', 'tax_code']),
-        ]
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._error_messages.update({
+            ]
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._error_messages.update({
             'modify': 'You can not modify tax from an invoice ' \
                     'that is opened, paid!',
             'create': 'You can not add a line to an invoice ' \
                     'that is open, paid or canceled!',
             })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(InvoiceTax, self).init(module_name)
+        super(InvoiceTax, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_base(self):
+    @staticmethod
+    def default_base():
         return Decimal('0.0')
 
-    def default_amount(self):
+    @staticmethod
+    def default_amount():
         return Decimal('0.0')
 
-    def default_manual(self):
+    @staticmethod
+    def default_manual():
         return True
 
-    def default_base_sign(self):
+    @staticmethod
+    def default_base_sign():
         return Decimal('1')
 
-    def default_tax_sign(self):
+    @staticmethod
+    def default_tax_sign():
         return Decimal('1')
 
-    def on_change_tax(self, values):
-        pool = Pool()
-        tax_obj = pool.get('account.tax')
-        invoice_obj = pool.get('account.invoice')
+    def on_change_tax(self):
+        Tax = Pool().get('account.tax')
         changes = {}
-        if values.get('tax'):
-            invoice_values = dict(
-                (f[16:], v) for f, v in values.iteritems()
-                if f.startswith('_parent_invoice.'))
-            context = invoice_obj.get_tax_context(invoice_values)
+        if self.tax:
+            if self.invoice:
+                context = self.invoice.get_tax_context()
+            else:
+                context = {}
             with Transaction().set_context(**context):
-                tax = tax_obj.browse(values['tax'])
+                tax = Tax(self.tax.id)
             changes['description'] = tax.description
-            invoice_type = values.get('_parent_invoice.type', 'out_invoice')
+            if self.invoice and self.invoice.type:
+                invoice_type = self.invoice.type
+            else:
+                invoice_type = 'out_invoice'
             if invoice_type in ('out_invoice', 'in_invoice'):
                 changes['base_code'] = tax.invoice_base_code.id
                 changes['base_sign'] = tax.invoice_base_sign
@@ -2094,92 +1918,85 @@
                 changes['account'] = tax.credit_note_account.id
         return changes
 
-    def on_change_with_amount(self, values):
-        pool = Pool()
-        tax_obj = pool.get('account.tax')
-        if values.get('tax') and values.get('manual', False):
-            tax_id = values['tax']
-            base = values.get('base') or Decimal(0)
-            for tax in tax_obj.compute([tax_id], base, 1):
-                if (tax['tax'].id == tax_id
-                        and tax['base'] == values.get('base')):
+    def on_change_with_amount(self):
+        Tax = Pool().get('account.tax')
+        if self.tax and self.manual:
+            tax = self.tax
+            base = self.base or Decimal(0)
+            for tax in Tax.compute([tax], base, 1):
+                if (tax['tax'] == tax
+                        and tax['base'] == self.base):
                     return tax['amount']
-        return values.get('amount')
+        return self.amount
 
-    def check_modify(self, ids):
+    @classmethod
+    def check_modify(cls, taxes):
         '''
         Check if the taxes can be modified
         '''
-        for tax in self.browse(ids):
+        for tax in taxes:
             if tax.invoice.state in ('open', 'paid'):
-                self.raise_user_error('modify')
-        return
+                cls.raise_user_error('modify')
 
-    def get_sequence_number(self, ids, name):
-        res = {}
-        for tax in self.browse(ids):
-            res[tax.id] = 0
-            i = 1
-            for tax2 in tax.invoice.taxes:
-                if tax2.id == tax.id:
-                    res[tax.id] = i
-                    break
-                i += 1
-        return res
+    def get_sequence_number(self, name):
+        i = 1
+        for tax in self.invoice.taxes:
+            if tax == self:
+                return i
+            i += 1
+        return 0
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        self.check_modify(ids)
-        return super(InvoiceTax, self).delete(ids)
+    @classmethod
+    def delete(cls, taxes):
+        cls.check_modify(taxes)
+        super(InvoiceTax, cls).delete(taxes)
 
-    def write(self, ids, vals):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        self.check_modify(ids)
-        return super(InvoiceTax, self).write(ids, vals)
+    @classmethod
+    def write(cls, taxes, vals):
+        cls.check_modify(taxes)
+        super(InvoiceTax, cls).write(taxes, vals)
 
-    def create(self, vals):
-        invoice_obj = Pool().get('account.invoice')
+    @classmethod
+    def create(cls, vals):
+        Invoice = Pool().get('account.invoice')
         if vals.get('invoice'):
-            invoice = invoice_obj.browse(vals['invoice'])
+            invoice = Invoice(vals['invoice'])
             if invoice.state in ('open', 'paid', 'cancel'):
-                self.raise_user_error('create')
-        return super(InvoiceTax, self).create(vals)
+                cls.raise_user_error('create')
+        return super(InvoiceTax, cls).create(vals)
 
-    def check_company(self, ids):
-        for tax in self.browse(ids):
-            company = tax.invoice.company
-            if tax.account.company.id != company.id:
+    def check_company(self):
+        company = self.invoice.company
+        if self.account.company != company:
+            return False
+        if self.base_code:
+            if self.base_code.company != company:
                 return False
-            if tax.base_code:
-                if tax.base_code.company.id != company.id:
-                    return False
-            if tax.tax_code:
-                if tax.tax_code.company.id != company.id:
-                    return False
+        if self.tax_code:
+            if self.tax_code.company != company:
+                return False
         return True
 
-    def get_move_line(self, tax):
+    def get_move_line(self):
         '''
         Return a list of move lines values for invoice tax
         '''
-        currency_obj = Pool().get('currency.currency')
+        Currency = Pool().get('currency.currency')
         res = {}
-        if not tax.amount:
+        if not self.amount:
             return []
-        res['name'] = tax.description
-        if tax.invoice.currency.id != tax.invoice.company.currency.id:
-            with Transaction().set_context(date=tax.invoice.currency_date):
-                amount = currency_obj.compute(tax.invoice.currency.id,
-                        tax.amount, tax.invoice.company.currency.id)
-            res['amount_second_currency'] = tax.amount
-            res['second_currency'] = tax.invoice.currency.id
+        res['name'] = self.description
+        if self.invoice.currency != self.invoice.company.currency:
+            with Transaction().set_context(date=self.invoice.currency_date):
+                amount = Currency.compute(self.invoice.currency, self.amount,
+                    self.invoice.company.currency)
+            res['amount_second_currency'] = self.amount
+            res['second_currency'] = self.invoice.currency.id
         else:
-            amount = tax.amount
+            amount = self.amount
             res['amount_second_currency'] = Decimal('0.0')
             res['second_currency'] = None
-        if tax.invoice.type in ('in_invoice', 'out_credit_note'):
+        if self.invoice.type in ('in_invoice', 'out_credit_note'):
             if amount >= Decimal('0.0'):
                 res['debit'] = amount
                 res['credit'] = Decimal('0.0')
@@ -2195,17 +2012,17 @@
             else:
                 res['debit'] = - amount
                 res['credit'] = Decimal('0.0')
-        res['account'] = tax.account.id
-        res['party'] = tax.invoice.party.id
-        if tax.tax_code:
+        res['account'] = self.account.id
+        res['party'] = self.invoice.party.id
+        if self.tax_code:
             res['tax_lines'] = [('create', {
-                'code': tax.tax_code.id,
-                'amount': amount * tax.tax_sign,
-                'tax': tax.tax and tax.tax.id or None
+                'code': self.tax_code.id,
+                'amount': amount * self.tax_sign,
+                'tax': self.tax and self.tax.id or None
             })]
         return [res]
 
-    def _credit(self, tax):
+    def _credit(self):
         '''
         Return values to credit tax.
         '''
@@ -2213,27 +2030,21 @@
 
         for field in ('description', 'sequence', 'base', 'amount',
                 'manual', 'base_sign', 'tax_sign'):
-            res[field] = tax[field]
+            res[field] = getattr(self, field)
 
         for field in ('account', 'base_code', 'tax_code', 'tax'):
-            res[field] = tax[field].id
+            res[field] = getattr(self, field).id
         return res
 
-InvoiceTax()
-
 
 class PrintInvoiceWarning(ModelView):
     'Print Invoice Report Warning'
-    _name = 'account.invoice.print.warning'
-    _description = __doc__
-
-PrintInvoiceWarning()
+    __name__ = 'account.invoice.print.warning'
 
 
 class PrintInvoice(Wizard):
     'Print Invoice Report'
-    _name = 'account.invoice.print'
-
+    __name__ = 'account.invoice.print'
     start = StateTransition()
     warning = StateView('account.invoice.print.warning',
         'account_invoice.print_warning_view_form', [
@@ -2242,81 +2053,79 @@
             ])
     print_ = StateAction('account_invoice.report_invoice')
 
-    def transition_start(self, session):
+    def transition_start(self):
         if len(Transaction().context['active_ids']) > 1:
             return 'warning'
         return 'print_'
 
-    def do_print_(self, session, action):
+    def do_print_(self, action):
         data = {}
         data['id'] = Transaction().context['active_ids'].pop()
         data['ids'] = [data['id']]
         return action, data
 
-    def transition_print_(self, session):
+    def transition_print_(self):
         if Transaction().context['active_ids']:
             return 'print_'
         return 'end'
 
-PrintInvoice()
-
 
 class InvoiceReport(Report):
-    _name = 'account.invoice'
+    __name__ = 'account.invoice'
 
-    def __init__(self):
-        super(InvoiceReport, self).__init__()
-        self._rpc['execute'] = True
+    @classmethod
+    def __setup__(cls):
+        super(InvoiceReport, cls).__setup__()
+        cls.__rpc__['execute'] = RPC(False)
 
-    def execute(self, ids, data):
-        invoice_obj = Pool().get('account.invoice')
+    @classmethod
+    def execute(cls, ids, data):
+        Invoice = Pool().get('account.invoice')
 
-        res = super(InvoiceReport, self).execute(ids, data)
+        res = super(InvoiceReport, cls).execute(ids, data)
         if len(ids) > 1:
             res = (res[0], res[1], True, res[3])
         else:
-            invoice = invoice_obj.browse(ids[0])
+            invoice = Invoice(ids[0])
             if invoice.number:
                 res = (res[0], res[1], res[2], res[3] + ' - ' + invoice.number)
         return res
 
-    def _get_objects(self, ids, model, data):
-        invoice_obj = Pool().get('account.invoice')
+    @classmethod
+    def _get_records(cls, ids, model, data):
+        with Transaction().set_context(language=False):
+            return super(InvoiceReport, cls)._get_records(ids[:1], model, data)
 
-        with Transaction().set_context(language=False):
-            return invoice_obj.browse([ids[0]])
+    @classmethod
+    def parse(cls, report, records, data, localcontext):
+        pool = Pool()
+        User = pool.get('res.user')
+        Invoice = pool.get('account.invoice')
 
-    def parse(self, report, objects, data, localcontext):
-        user_obj = Pool().get('res.user')
-        invoice_obj = Pool().get('account.invoice')
-
-        invoice = objects[0]
+        invoice = records[0]
 
         if invoice.invoice_report_cache:
             return (invoice.invoice_report_format,
                 invoice.invoice_report_cache)
 
-        user = user_obj.browse(Transaction().user)
+        user = User(Transaction().user)
         localcontext['company'] = user.company
-        res = super(InvoiceReport, self).parse(report, objects, data,
+        res = super(InvoiceReport, cls).parse(report, records, data,
                 localcontext)
         # If the invoice is open or paid and the report not saved in
         # invoice_report_cache there was an error somewhere. So we save it now
         # in invoice_report_cache
         if invoice.state in ('open', 'paid'):
-            invoice_obj.write(invoice.id, {
+            Invoice.write([invoice], {
                 'invoice_report_format': res[0],
                 'invoice_report_cache': res[1],
                 })
         return res
 
-InvoiceReport()
-
 
 class PayInvoiceStart(ModelView):
     'Pay Invoice'
-    _name = 'account.invoice.pay.start'
-    _description = __doc__
+    __name__ = 'account.invoice.pay.start'
     amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)),
         depends=['currency_digits'], required=True)
     currency = fields.Many2One('currency.currency', 'Currency', required=True)
@@ -2327,27 +2136,24 @@
             domain=[('type', '=', 'cash')])
     date = fields.Date('Date', required=True)
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
-    def default_currency_digits(self):
+    @staticmethod
+    def default_currency_digits():
         return 2
 
-    def on_change_with_currency_digits(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-            return currency.digits
+    def on_change_with_currency_digits(self):
+        if self.currency:
+            return self.currency.digits
         return 2
 
-PayInvoiceStart()
-
 
 class PayInvoiceAsk(ModelView):
     'Pay Invoice'
-    _name = 'account.invoice.pay.ask'
-    _description = __doc__
+    __name__ = 'account.invoice.pay.ask'
     type = fields.Selection([
         ('writeoff', 'Write-Off'),
         ('partial', 'Partial Payment'),
@@ -2403,37 +2209,31 @@
     company = fields.Many2One('company.company', 'Company', readonly=True)
     invoice = fields.Many2One('account.invoice', 'Invoice', readonly=True)
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'partial'
 
-    def on_change_lines(self, vals):
-        pool = Pool()
-        currency_obj = pool.get('currency.currency')
-        line_obj = pool.get('account.move.line')
-        invoice_obj = pool.get('account.invoice')
+    def on_change_lines(self):
+        Currency = Pool().get('currency.currency')
 
         res = {}
-        invoice = invoice_obj.browse(vals['invoice'])
-        with Transaction().set_context(date=invoice.currency_date):
-            amount = currency_obj.compute(vals['currency'], vals['amount'],
-                    vals['currency_writeoff'])
+        with Transaction().set_context(date=self.invoice.currency_date):
+            amount = Currency.compute(self.currency, self.amount,
+                self.currency_writeoff)
 
         res['amount_writeoff'] = Decimal('0.0')
-        for line in line_obj.browse(vals['lines']):
+        for line in self.lines:
             res['amount_writeoff'] += line.debit - line.credit
-        if invoice.type in ('in_invoice', 'out_credit_note'):
+        if self.invoice.type in ('in_invoice', 'out_credit_note'):
             res['amount_writeoff'] = - res['amount_writeoff'] - amount
         else:
             res['amount_writeoff'] = res['amount_writeoff'] - amount
         return res
 
-PayInvoiceAsk()
-
 
 class PayInvoice(Wizard):
     'Pay Invoice'
-    _name = 'account.invoice.pay'
-
+    __name__ = 'account.invoice.pay'
     start = StateView('account.invoice.pay.start',
         'account_invoice.pay_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -2447,17 +2247,18 @@
             ])
     pay = StateTransition()
 
-    def __init__(self):
-        super(PayInvoice, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(PayInvoice, cls).__setup__()
+        cls._error_messages.update({
             'amount_greater_amount_to_pay': 'You can not create a partial ' \
                     'payment with an amount greater then the amount to pay!',
             })
 
-    def default_start(self, session, fields):
-        invoice_obj = Pool().get('account.invoice')
+    def default_start(self, fields):
+        Invoice = Pool().get('account.invoice')
         default = {}
-        invoice = invoice_obj.browse(Transaction().context['active_id'])
+        invoice = Invoice(Transaction().context['active_id'])
         default['currency'] = invoice.currency.id
         default['currency_digits'] = invoice.currency.digits
         default['amount'] = (invoice.amount_to_pay_today
@@ -2465,52 +2266,52 @@
         default['description'] = invoice.number
         return default
 
-    def transition_choice(self, session):
-        invoice_obj = Pool().get('account.invoice')
-        currency_obj = Pool().get('currency.currency')
+    def transition_choice(self):
+        pool = Pool()
+        Invoice = pool.get('account.invoice')
+        Currency = pool.get('currency.currency')
 
-        invoice = invoice_obj.browse(Transaction().context['active_id'])
+        invoice = Invoice(Transaction().context['active_id'])
 
-        with Transaction().set_context(date=session.start.date):
-            amount = currency_obj.compute(session.start.currency.id,
-                session.start.amount, invoice.company.currency.id)
-        _, remainder = invoice_obj.get_reconcile_lines_for_amount(invoice,
-            amount)
+        with Transaction().set_context(date=self.start.date):
+            amount = Currency.compute(self.start.currency,
+                self.start.amount, invoice.company.currency)
+        _, remainder = invoice.get_reconcile_lines_for_amount(amount)
         if remainder == Decimal('0.0') and amount <= invoice.amount_to_pay:
             return 'pay'
         return 'ask'
 
-    def default_ask(self, session, fields):
+    def default_ask(self, fields):
         pool = Pool()
-        invoice_obj = pool.get('account.invoice')
-        currency_obj = pool.get('currency.currency')
-        line_obj = pool.get('account.move.line')
+        Invoice = pool.get('account.invoice')
+        Currency = pool.get('currency.currency')
+        Line = pool.get('account.move.line')
 
         default = {}
-        invoice = invoice_obj.browse(Transaction().context['active_id'])
+        invoice = Invoice(Transaction().context['active_id'])
         default['lines_to_pay'] = [x.id for x in invoice.lines_to_pay
                 if not x.reconciliation]
 
-        default['amount'] = session.start.amount
-        default['currency'] = session.start.currency.id
-        default['currency_digits'] = session.start.currency_digits
+        default['amount'] = self.start.amount
+        default['currency'] = self.start.currency.id
+        default['currency_digits'] = self.start.currency_digits
         default['company'] = invoice.company.id
 
-        with Transaction().set_context(date=session.start.date):
-            amount = currency_obj.compute(session.start.currency.id,
-                session.start.amount, invoice.company.currency.id)
+        with Transaction().set_context(date=self.start.date):
+            amount = Currency.compute(self.start.currency.id,
+                self.start.amount, invoice.company.currency.id)
 
-        if currency_obj.is_zero(invoice.company.currency, amount):
+        if invoice.company.currency.is_zero(amount):
             default['lines'] = [x.id for x in invoice.lines_to_pay]
         else:
             default['lines'], _ = \
-                invoice_obj.get_reconcile_lines_for_amount(invoice, amount)
+                invoice.get_reconcile_lines_for_amount(amount)
         for line_id in default['lines'][:]:
             if line_id not in default['lines_to_pay']:
                 default['lines'].remove(line_id)
 
         default['amount_writeoff'] = Decimal('0.0')
-        for line in line_obj.browse(default['lines']):
+        for line in Line.browse(default['lines']):
             default['amount_writeoff'] += line.debit - line.credit
         for line in invoice.payment_lines:
             default['amount_writeoff'] += line.debit - line.credit
@@ -2525,81 +2326,75 @@
         default['payment_lines'] = [x.id for x in invoice.payment_lines
                 if not x.reconciliation]
 
-        if amount > invoice.amount_to_pay \
-                or currency_obj.is_zero(invoice.company.currency, amount):
+        if (amount > invoice.amount_to_pay
+                or invoice.company.currency.is_zero(amount)):
             default['type'] = 'writeoff'
         return default
 
-    def transition_pay(self, session):
+    def transition_pay(self):
         pool = Pool()
-        invoice_obj = pool.get('account.invoice')
-        currency_obj = pool.get('currency.currency')
-        move_line_obj = pool.get('account.move.line')
+        Invoice = pool.get('account.invoice')
+        Currency = pool.get('currency.currency')
+        MoveLine = pool.get('account.move.line')
 
-        invoice = invoice_obj.browse(Transaction().context['active_id'])
+        invoice = Invoice(Transaction().context['active_id'])
 
-        with Transaction().set_context(date=session.start.date):
-            amount = currency_obj.compute(session.start.currency.id,
-                session.start.amount, invoice.company.currency.id)
+        with Transaction().set_context(date=self.start.date):
+            amount = Currency.compute(self.start.currency,
+                self.start.amount, invoice.company.currency)
 
         reconcile_lines, remainder = \
-            invoice_obj.get_reconcile_lines_for_amount(invoice, amount)
+            invoice.get_reconcile_lines_for_amount(amount)
 
         amount_second_currency = None
         second_currency = None
-        if session.start.currency.id != invoice.company.currency.id:
-            amount_second_currency = session.start.amount
-            second_currency = session.start.currency
+        if self.start.currency != invoice.company.currency:
+            amount_second_currency = self.start.amount
+            second_currency = self.start.currency
 
         if amount > invoice.amount_to_pay and \
-                session.ask.type != 'writeoff':
+                self.ask.type != 'writeoff':
             self.raise_user_error('amount_greater_amount_to_pay')
 
-        line_id = None
-        if not currency_obj.is_zero(invoice.company.currency, amount):
-            line_id = invoice_obj.pay_invoice(invoice.id, amount,
-                session.start.journal.id, session.start.date,
-                session.start.description, amount_second_currency,
+        line = None
+        if not invoice.company.currency.is_zero(amount):
+            line = invoice.pay_invoice(amount,
+                self.start.journal, self.start.date,
+                self.start.description, amount_second_currency,
                 second_currency)
 
         if remainder != Decimal('0.0'):
-            if session.ask.type == 'writeoff':
-                line_ids = [x.id for x in session.ask.lines] + \
-                    [x.id for x in invoice.payment_lines
-                        if not x.reconciliation]
-                if line_id and line_id not in line_ids:
+            if self.ask.type == 'writeoff':
+                lines = [l for l in self.ask.lines] + \
+                    [l for l in invoice.payment_lines
+                        if not l.reconciliation]
+                if line and line not in lines:
                     # Add new payment line if payment_lines was cached before
                     # its creation
-                    line_ids += [line_id]
-                if line_ids:
-                    move_line_obj.reconcile(line_ids,
-                        journal_id=session.ask.journal_writeoff.id,
-                        date=session.start.date,
-                        account_id=session.ask.account_writeoff.id)
+                    lines += [line]
+                if lines:
+                    MoveLine.reconcile(lines,
+                        journal=self.ask.journal_writeoff,
+                        date=self.start.date,
+                        account=self.ask.account_writeoff)
         else:
-            if line_id:
-                reconcile_lines += [line_id]
+            if line:
+                reconcile_lines += [line]
             if reconcile_lines:
-                move_line_obj.reconcile(reconcile_lines)
+                MoveLine.reconcile(reconcile_lines)
         return 'end'
 
-PayInvoice()
-
 
 class CreditInvoiceStart(ModelView):
     'Credit Invoice Init'
-    _name = 'account.invoice.credit.start'
-    _description = __doc__
+    __name__ = 'account.invoice.credit.start'
     with_refund = fields.Boolean('With Refund', help='If true, ' \
             'the current invoice(s) will be paid.')
 
-CreditInvoiceStart()
-
 
 class CreditInvoice(Wizard):
     'Credit Invoice'
-    _name = 'account.invoice.credit'
-
+    __name__ = 'account.invoice.credit'
     start = StateView('account.invoice.credit.start',
         'account_invoice.credit_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -2607,45 +2402,44 @@
             ])
     credit = StateAction('account_invoice.act_invoice_form')
 
-    def __init__(self):
-        super(CreditInvoice, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(CreditInvoice, cls).__setup__()
+        cls._error_messages.update({
             'refund_non_open': 'You can not credit with refund ' \
                     'an invoice that is not opened!',
             'refund_with_payement': 'You can not credit with refund ' \
                     'an invoice with payments!',
             })
 
-    def default_start(self, session, fields):
-        invoice_obj = Pool().get('account.invoice')
+    def default_start(self, fields):
+        Invoice = Pool().get('account.invoice')
         default = {
             'with_refund': True,
-        }
-        for invoice in invoice_obj.browse(Transaction().context['active_ids']):
+            }
+        for invoice in Invoice.browse(Transaction().context['active_ids']):
             if invoice.state != 'open' or invoice.payment_lines:
                 default['with_refund'] = False
                 break
         return default
 
-    def do_credit(self, session, action):
+    def do_credit(self, action):
         pool = Pool()
-        invoice_obj = pool.get('account.invoice')
+        Invoice = pool.get('account.invoice')
 
-        refund = session.start.with_refund
-        invoice_ids = Transaction().context['active_ids']
+        refund = self.start.with_refund
+        invoices = Invoice.browse(Transaction().context['active_ids'])
 
         if refund:
-            for invoice in invoice_obj.browse(invoice_ids):
+            for invoice in invoices:
                 if invoice.state != 'open':
                     self.raise_user_error('refund_non_open')
                 if invoice.payment_lines:
                     self.raise_user_error('refund_with_payement')
 
-        invoice_ids = invoice_obj.credit(invoice_ids, refund=refund)
+        invoice_ids = Invoice.credit(invoices, refund=refund)
 
         data = {'res_id': invoice_ids}
         if len(invoice_ids) == 1:
             action['views'].reverse()
         return action, data
-
-CreditInvoice()
diff -r e32ff462bf6e trytond/modules/account_invoice/party.py
--- a/trytond/modules/account_invoice/party.py	Mon Sep 10 15:27:42 2012 +0200
+++ b/trytond/modules/account_invoice/party.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,37 +1,37 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelView, ModelSQL, fields
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Address', 'Party']
+__metaclass__ = PoolMeta
 
-class Address(ModelSQL, ModelView):
-    _name = 'party.address'
+
+class Address:
+    __name__ = 'party.address'
     invoice = fields.Boolean('Invoice')
 
-Address()
-
 
 class Party(ModelSQL, ModelView):
-    _name = 'party.party'
+    __name__ = 'party.party'
     customer_payment_term = fields.Property(fields.Many2One(
         'account.invoice.payment_term', string='Customer Payment Term'))
     supplier_payment_term = fields.Property(fields.Many2One(
         'account.invoice.payment_term', string='Supplier Payment Term'))
 
-    def init(self, module_name):
-        ir_model_field_obj = Pool().get('ir.model.field')
+    @classmethod
+    def __register__(cls, module_name):
+        ModelField = Pool().get('ir.model.field')
 
         # Migration from 2.2: property field payment_term renamed
         # to customer_payment_term
-        field_ids = ir_model_field_obj.search([
+        fields = ModelField.search([
                 ('name', '=', 'payment_term'),
                 ('model.model', '=', 'party.party')
                 ])
-        if field_ids:
-            ir_model_field_obj.write(field_ids, {
+        if fields:
+            ModelField.write(fields, {
                     'name': 'customer_payment_term',
                     })
 
-        super(Party, self).init(module_name)
-
-Party()
+        super(Party, cls).__register__(module_name)
diff -r e32ff462bf6e trytond/modules/account_invoice/payment_term.py
--- a/trytond/modules/account_invoice/payment_term.py	Mon Sep 10 15:27:42 2012 +0200
+++ b/trytond/modules/account_invoice/payment_term.py	Mon Sep 24 20:36:39 2012 +0200
@@ -8,11 +8,12 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['PaymentTerm', 'PaymentTermLine']
+
 
 class PaymentTerm(ModelSQL, ModelView):
     'Payment Term'
-    _name = 'account.invoice.payment_term'
-    _description = __doc__
+    __name__ = 'account.invoice.payment_term'
     name = fields.Char('Payment Term', size=None, required=True,
         translate=True)
     active = fields.Boolean('Active')
@@ -20,41 +21,39 @@
     lines = fields.One2Many('account.invoice.payment_term.line', 'payment',
             'Lines')
 
-    def __init__(self):
-        super(PaymentTerm, self).__init__()
-        self._order.insert(0, ('name', 'ASC'))
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(PaymentTerm, cls).__setup__()
+        cls._order.insert(0, ('name', 'ASC'))
+        cls._error_messages.update({
             'invalid_line': 'Invalid payment term line!',
             'missing_remainder': 'Payment term missing a remainder line!',
             })
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def compute(self, amount, currency, payment_term, date=None):
+    def compute(self, amount, currency, date=None):
         """Calculate payment terms and return a list of tuples
         with (date, amount) for each payment term line.
 
-        amount must be a Decimal used for the calculation and
-        both currency and payment_term must be BrowseRecord. If
-        specified, date will be used as the start date, otherwise
-        current date will be used.
+        amount must be a Decimal used for the calculation.
+        If specified, date will be used as the start date, otherwise current
+        date will be used.
         """
         #TODO implement business_days
         # http://pypi.python.org/pypi/BusinessHours/
-        pool = Pool()
-        line_obj = pool.get('account.invoice.payment_term.line')
-        currency_obj = pool.get('currency.currency')
-        date_obj = pool.get('ir.date')
+        Date = Pool().get('ir.date')
 
         sign = 1 if amount >= Decimal('0.0') else -1
         res = []
         if date is None:
-            date = date_obj.today()
+            date = Date.today()
         remainder = amount
-        for line in payment_term.lines:
-            value = line_obj.get_value(line, remainder, amount, currency)
-            value_date = line_obj.get_date(line, date)
+        for line in self.lines:
+            value = line.get_value(remainder, amount, currency)
+            value_date = line.get_date(date)
             if not value or not value_date:
                 if (not remainder) and line.amount:
                     self.raise_user_error('invalid_line')
@@ -66,17 +65,14 @@
             res.append((value_date, value))
             remainder -= value
 
-        if not currency_obj.is_zero(currency, remainder):
+        if not currency.is_zero(remainder):
             self.raise_user_error('missing_remainder')
         return res
 
-PaymentTerm()
-
 
 class PaymentTermLine(ModelSQL, ModelView):
     'Payment Term Line'
-    _name = 'account.invoice.payment_term.line'
-    _description = __doc__
+    __name__ = 'account.invoice.payment_term.line'
     sequence = fields.Integer('Sequence',
         order_field='(%(table)s.sequence IS NULL) %(order)s, '
         '%(table)s.sequence %(order)s',
@@ -111,7 +107,7 @@
             'required': Eval('type') == 'fixed',
             }, depends=['type'])
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['currency']), 'get_currency_digits')
+        on_change_with=['currency']), 'on_change_with_currency_digits')
     day = fields.Integer('Day of Month')
     month = fields.Selection([
             (None, ''),
@@ -142,132 +138,126 @@
     weeks = fields.Integer('Number of Weeks', required=True)
     days = fields.Integer('Number of Days', required=True)
 
-    def __init__(self):
-        super(PaymentTermLine, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(PaymentTermLine, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._sql_constraints += [
             ('day', 'CHECK(day BETWEEN 1 AND 31)',
                 'Day of month must be between 1 and 31.'),
             ]
-        self._constraints += [
+        cls._constraints += [
             ('check_percentage_and_divisor', 'invalid_percentage_and_divisor'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
                 'invalid_percentage_and_divisor': 'Percentage and '
                         'Divisor values are not consistent.',
                 })
 
-    def init(self, module_name):
-        super(PaymentTermLine, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(PaymentTermLine, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 percent change into percentage
         if table.column_exist('percent'):
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET percentage = percent * 100')
             table.drop_column('percent', exception=True)
 
         # Migration from 2.2
         if table.column_exist('delay'):
-            cursor.execute('UPDATE "' + self._table + '" SET day = 31 '
+            cursor.execute('UPDATE "' + cls._table + '" SET day = 31 '
                 "WHERE delay = 'end_month'")
             table.drop_column('delay', exception=True)
-            ids = self.search([])
-            for line in self.browse(ids):
+            lines = cls.search([])
+            for line in lines:
                 if line.percentage:
-                    self.write(line.id, {
-                            'divisor': self.round(Decimal('100.0') /
-                                line.percentage, self.divisor.digits[1]),
+                    cls.write([line], {
+                            'divisor': cls.round(Decimal('100.0') /
+                                line.percentage, cls.divisor.digits[1]),
                             })
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_currency_digits(self):
+    @staticmethod
+    def default_currency_digits():
         return 2
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'remainder'
 
-    def default_months(self):
+    @staticmethod
+    def default_months():
         return 0
 
-    def default_weeks(self):
+    @staticmethod
+    def default_weeks():
         return 0
 
-    def default_days(self):
+    @staticmethod
+    def default_days():
         return 0
 
-    def on_change_type(self, vals):
-        if not 'type' in vals:
-            return {}
+    def on_change_type(self):
         res = {}
-        if vals['type'] != 'fixed':
+        if self.type != 'fixed':
             res['amount'] = Decimal('0.0')
             res['currency'] = None
-        if vals['type'] not in ('percent', 'percent_on_total'):
+        if self.type not in ('percent', 'percent_on_total'):
             res['percentage'] = Decimal('0.0')
             res['divisor'] = Decimal('0.0')
         return res
 
-    def on_change_percentage(self, value):
-        if not value.get('percentage'):
+    def on_change_percentage(self):
+        if not self.percentage:
             return {'divisor': 0.0}
         return {
-            'divisor': self.round(Decimal('100.0') / value['percentage'],
-                self.divisor.digits[1]),
+            'divisor': self.round(Decimal('100.0') / self.percentage,
+                self.__class__.divisor.digits[1]),
             }
 
-    def on_change_divisor(self, value):
-        if not value.get('divisor'):
+    def on_change_divisor(self):
+        if not self.divisor:
             return {'percentage': 0.0}
         return {
-            'percentage': self.round(Decimal('100.0') / value['divisor'],
-                self.percentage.digits[1]),
+            'percentage': self.round(Decimal('100.0') / self.divisor,
+                self.__class__.percentage.digits[1]),
             }
 
-    def on_change_with_currency_digits(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-            return currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.currency:
+            return self.currency.digits
         return 2
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            if line.currency:
-                res[line.id] = line.currency.digits
-            else:
-                res[line.id] = 2
-        return res
-
-    def get_delta(self, line):
+    def get_delta(self):
         return {
-            'day': line.day,
-            'month': int(line.month) if line.month else None,
-            'days': line.days,
-            'weeks': line.weeks,
-            'months': line.months,
-            'weekday': int(line.weekday) if line.weekday else None,
+            'day': self.day,
+            'month': int(self.month) if self.month else None,
+            'days': self.days,
+            'weeks': self.weeks,
+            'months': self.months,
+            'weekday': int(self.weekday) if self.weekday else None,
             }
 
-    def get_date(self, line, date):
-        return date + relativedelta(**self.get_delta(line))
+    def get_date(self, date):
+        return date + relativedelta(**self.get_delta())
 
-    def get_value(self, line, remainder, amount, currency):
-        currency_obj = Pool().get('currency.currency')
-        if line.type == 'fixed':
-            return currency_obj.compute(line.currency, line.amount, currency)
-        elif line.type == 'percent':
-            return currency_obj.round(currency,
-                remainder * line.percentage / Decimal('100'))
-        elif line.type == 'percent_on_total':
-            return currency_obj.round(currency,
-                amount * line.percentage / Decimal('100'))
-        elif line.type == 'remainder':
-            return currency_obj.round(currency, remainder)
+    def get_value(self, remainder, amount, currency):
+        Currency = Pool().get('currency.currency')
+        if self.type == 'fixed':
+            return Currency.compute(self.currency, self.amount, currency)
+        elif self.type == 'percent':
+            return currency.round(
+                remainder * self.percentage / Decimal('100'))
+        elif self.type == 'percent_on_total':
+            return currency.round(
+                amount * self.percentage / Decimal('100'))
+        elif self.type == 'remainder':
+            return currency.round(remainder)
         return None
 
     @staticmethod
@@ -275,11 +265,12 @@
         quantize = Decimal(10) ** -Decimal(digits)
         return Decimal(number).quantize(quantize)
 
-    def check_percentage_and_divisor(self, ids):
+    @classmethod
+    def check_percentage_and_divisor(cls, lines):
         "Check consistency between percentage and divisor"
-        percentage_digits = self.percentage.digits[1]
-        divisor_digits = self.divisor.digits[1]
-        for line in self.browse(ids):
+        percentage_digits = cls.percentage.digits[1]
+        divisor_digits = cls.divisor.digits[1]
+        for line in lines:
             if line.type not in ('percent', 'percent_on_total'):
                 continue
             if line.percentage is None or line.divisor is None:
@@ -288,14 +279,12 @@
                 continue
             percentage = line.percentage
             divisor = line.divisor
-            calc_percentage = self.round(Decimal('100.0') / divisor,
+            calc_percentage = cls.round(Decimal('100.0') / divisor,
                     percentage_digits)
-            calc_divisor = self.round(Decimal('100.0') / percentage,
+            calc_divisor = cls.round(Decimal('100.0') / percentage,
                     divisor_digits)
             if (percentage == Decimal('0.0') or divisor == Decimal('0.0')
                     or percentage != calc_percentage
                     and divisor != calc_divisor):
                 return False
         return True
-
-PaymentTermLine()
diff -r e32ff462bf6e trytond/modules/account_invoice/tests/test_account_invoice.py
--- a/trytond/modules/account_invoice/tests/test_account_invoice.py	Mon Sep 10 15:27:42 2012 +0200
+++ b/trytond/modules/account_invoice/tests/test_account_invoice.py	Mon Sep 24 20:36:39 2012 +0200
@@ -45,14 +45,13 @@
         Test payment_term.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            cu1_id = self.currency.create({
+            cu1 = self.currency.create({
                     'name': 'cu1',
                     'symbol': 'cu1',
                     'code': 'cu1'
                     })
-            cu1 = self.currency.browse(cu1_id)
 
-            term_id = self.payment_term.create({
+            term = self.payment_term.create({
                     'name': '30 days, 1 month, 1 month + 15 days',
                     'lines': [
                         ('create', {
@@ -75,7 +74,7 @@
                                 'months': 1,
                                 'days': 30,
                                 'amount': Decimal('396.84'),
-                                'currency': cu1_id,
+                                'currency': cu1.id,
                                 }),
                         ('create', {
                                 'sequence': 3,
@@ -85,9 +84,8 @@
                                 'day': 15,
                                 })]
                     })
-            term = self.payment_term.browse(term_id)
-            terms = term.compute(Decimal('1587.35'), cu1, term,
-                    date=datetime.date(2011, 10, 1))
+            terms = term.compute(Decimal('1587.35'), cu1,
+                date=datetime.date(2011, 10, 1))
             self.assertEqual(terms, [
                     (datetime.date(2011, 10, 31), Decimal('396.84')),
                     (datetime.date(2011, 11, 01), Decimal('396.84')),
diff -r ccb27f24a097 trytond/modules/account_invoice_history/__init__.py
--- a/trytond/modules/account_invoice_history/__init__.py	Tue Sep 04 23:04:16 2012 +0200
+++ b/trytond/modules/account_invoice_history/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,6 +1,17 @@
 #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.pool import Pool
 from .party import *
 from .payment_term import *
 from .invoice import *
+
+
+def register():
+    Pool.register(
+        Party,
+        Address,
+        PaymentTerm,
+        PaymentTermLine,
+        Invoice,
+        module='account_invoice_history', type_='model')
diff -r ccb27f24a097 trytond/modules/account_invoice_history/invoice.py
--- a/trytond/modules/account_invoice_history/invoice.py	Tue Sep 04 23:04:16 2012 +0200
+++ b/trytond/modules/account_invoice_history/invoice.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,49 +1,43 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 import datetime
-import copy
-from trytond.model import Model, fields
 
+from trytond.model import fields
+from trytond.pool import PoolMeta
 
-class Invoice(Model):
-    _name = 'account.invoice'
+__all__ = ['Invoice']
+__metaclass__ = PoolMeta
+
+
+class Invoice:
+    __name__ = 'account.invoice'
     open_date = fields.DateTime('Open Date')
 
-    def __init__(self):
-        super(Invoice, self).__init__()
-        self.party = copy.copy(self.party)
-        self.party.datetime_field = 'open_date'
-        if 'open_date' not in self.party.depends:
-            self.party.depends = copy.copy(self.party.depends)
-            self.party.depends.append('open_date')
-        self.invoice_address = copy.copy(self.invoice_address)
-        self.invoice_address.datetime_field = 'open_date'
-        if 'open_date' not in self.invoice_address.depends:
-            self.invoice_address.depends = copy.copy(
-                    self.invoice_address.depends)
-            self.invoice_address.depends.append('open_date')
-        self.payment_term = copy.copy(self.payment_term)
-        self.payment_term.datetime_field = 'open_date'
-        if 'open_date' not in self.payment_term.depends:
-            self.payment_term.depends = copy.copy(
-                    self.payment_term.depends)
-            self.payment_term.depends.append('open_date')
-        self._reset_columns()
+    @classmethod
+    def __setup__(cls):
+        super(Invoice, cls).__setup__()
+        cls.party.datetime_field = 'open_date'
+        if 'open_date' not in cls.party.depends:
+            cls.party.depends.append('open_date')
+        cls.invoice_address.datetime_field = 'open_date'
+        if 'open_date' not in cls.invoice_address.depends:
+            cls.invoice_address.depends.append('open_date')
+        cls.payment_term.datetime_field = 'open_date'
+        if 'open_date' not in cls.payment_term.depends:
+            cls.payment_term.depends.append('open_date')
 
-    def set_number(self, invoice):
-        set_open_date = not invoice.number
-        res = super(Invoice, self).set_number(invoice)
+    def set_number(self):
+        set_open_date = not self.number
+        super(Invoice, self).set_number()
         if set_open_date:
-            self.write(invoice.id, {
-                'open_date': datetime.datetime.now(),
-                })
-        return res
+            self.write([self], {
+                    'open_date': datetime.datetime.now(),
+                    })
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, invoices, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default['open_date'] = None
-        return super(Invoice, self).copy(ids, default=default)
-
-Invoice()
+        return super(Invoice, cls).copy(invoices, default=default)
diff -r ccb27f24a097 trytond/modules/account_invoice_history/party.py
--- a/trytond/modules/account_invoice_history/party.py	Tue Sep 04 23:04:16 2012 +0200
+++ b/trytond/modules/account_invoice_history/party.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,17 +1,16 @@
 #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 ModelView, ModelSQL
+from trytond.pool import PoolMeta
 
+__all__ = ['Party', 'Address']
+__metaclass__ = PoolMeta
 
-class Party(ModelSQL, ModelView):
-    _name = 'party.party'
+
+class Party:
+    __name__ = 'party.party'
     _history = True
 
-Party()
 
-
-class Address(ModelSQL, ModelView):
-    _name = 'party.address'
+class Address:
+    __name__ = 'party.address'
     _history = True
-
-Address()
diff -r ccb27f24a097 trytond/modules/account_invoice_history/payment_term.py
--- a/trytond/modules/account_invoice_history/payment_term.py	Tue Sep 04 23:04:16 2012 +0200
+++ b/trytond/modules/account_invoice_history/payment_term.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,17 +1,16 @@
 #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 ModelView, ModelSQL
+from trytond.pool import PoolMeta
 
+__all__ = ['PaymentTerm', 'PaymentTermLine']
+__metaclass__ = PoolMeta
 
-class PaymentTerm(ModelSQL, ModelView):
-    _name = 'account.invoice.payment_term'
+
+class PaymentTerm:
+    __name__ = 'account.invoice.payment_term'
     _history = True
 
-PaymentTerm()
 
-
-class PaymentTermLine(ModelSQL, ModelView):
-    _name = 'account.invoice.payment_term.line'
+class PaymentTermLine:
+    __name__ = 'account.invoice.payment_term.line'
     _history = True
-
-PaymentTermLine()
diff -r b3d1f8ddf175 trytond/modules/account_invoice_line_standalone/__init__.py
--- a/trytond/modules/account_invoice_line_standalone/__init__.py	Fri Jun 08 19:33:37 2012 +0200
+++ b/trytond/modules/account_invoice_line_standalone/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,4 +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.pool import Pool
 from .invoice import *
+
+
+def register():
+    Pool.register(
+        Invoice,
+        InvoiceLine,
+        module='account_invoice_line_standalone', type_='model')
diff -r b3d1f8ddf175 trytond/modules/account_invoice_line_standalone/invoice.py
--- a/trytond/modules/account_invoice_line_standalone/invoice.py	Fri Jun 08 19:33:37 2012 +0200
+++ b/trytond/modules/account_invoice_line_standalone/invoice.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,17 +1,19 @@
 #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 copy
-from trytond.model import ModelView, ModelSQL
 from trytond.pyson import Eval
 from trytond.transaction import Transaction
+from trytond.pool import PoolMeta
 
+__all__ = ['Invoice', 'InvoiceLine']
+__metaclass__ = PoolMeta
 
-class Invoice(ModelSQL, ModelView):
-    _name = 'account.invoice'
 
-    def __init__(self):
-        super(Invoice, self).__init__()
-        self.lines = copy.copy(self.lines)
+class Invoice:
+    __name__ = 'account.invoice'
+
+    @classmethod
+    def __setup__(cls):
+        super(Invoice, cls).__setup__()
         add_remove = [
             ('invoice_type', '=', Eval('type')),
             ('party', '=', Eval('party')),
@@ -20,28 +22,23 @@
             ('invoice', '=', None),
         ]
 
-        if not self.lines.add_remove:
-            self.lines.add_remove = add_remove
+        if not cls.lines.add_remove:
+            cls.lines.add_remove = add_remove
         else:
-            self.lines.add_remove = copy.copy(self.lines.add_remove)
-            self.lines.add_remove = [
+            cls.lines.add_remove = [
                 add_remove,
-                self.lines.add_remove,
-            ]
-        self._reset_columns()
+                cls.lines.add_remove,
+                ]
 
-Invoice()
 
+class InvoiceLine:
+    __name__ = 'account.invoice.line'
 
-class InvoiceLine(ModelSQL, ModelView):
-    _name = 'account.invoice.line'
-
-    def _view_look_dom_arch(self, tree, type, field_children=None):
+    @classmethod
+    def _view_look_dom_arch(cls, tree, type, field_children=None):
         if type == 'form' and Transaction().context.get('standalone'):
             tree_root = tree.getroottree().getroot()
             if tree_root.get('cursor') == 'product':
                 tree_root.set('cursor', 'party')
-        return super(InvoiceLine, self)._view_look_dom_arch(tree, type,
+        return super(InvoiceLine, cls)._view_look_dom_arch(tree, type,
             field_children=field_children)
-
-InvoiceLine()
diff -r 5810df9693e2 trytond/modules/account_product/__init__.py
--- a/trytond/modules/account_product/__init__.py	Fri Jun 08 19:33:38 2012 +0200
+++ b/trytond/modules/account_product/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,4 +1,16 @@
 #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.pool import Pool
 from .product import *
+
+
+def register():
+    Pool.register(
+        Category,
+        CategoryCustomerTax,
+        CategorySupplierTax,
+        Template,
+        TemplateCustomerTax,
+        TemplateSupplierTax,
+        module='account_product', type_='model')
diff -r 5810df9693e2 trytond/modules/account_product/product.py
--- a/trytond/modules/account_product/product.py	Fri Jun 08 19:33:38 2012 +0200
+++ b/trytond/modules/account_product/product.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,16 +1,18 @@
 #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 copy
-from trytond.model import ModelView, ModelSQL, fields
+from trytond.model import ModelSQL, fields
 from trytond.pyson import Eval, Or
 from trytond.backend import TableHandler
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import PoolMeta
 
+__all__ = ['Category', 'CategoryCustomerTax', 'CategorySupplierTax',
+    'Template', 'TemplateCustomerTax', 'TemplateSupplierTax']
+__metaclass__ = PoolMeta
 
-class Category(ModelSQL, ModelView):
-    _name = 'product.category'
 
+class Category:
+    __name__ = 'product.category'
     account_parent = fields.Boolean('Use Parent\'s accounts',
         help='Use the accounts defined on the parent category')
     account_expense = fields.Property(fields.Many2One('account.account',
@@ -58,117 +60,96 @@
     supplier_taxes_used = fields.Function(fields.One2Many('account.tax', None,
             'Supplier Taxes Used'), 'get_taxes')
 
-    def __init__(self):
-        super(Category, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Category, cls).__setup__()
+        cls._error_messages.update({
             'missing_account': ('There is no account '
                     'expense/revenue defined on the category '
                     '%s (%d)'),
             })
-        self.parent = copy.copy(self.parent)
-        self.parent.states = copy.copy(self.parent.states)
-        self.parent.states['required'] = Or(
-            self.parent.states.get('required', False),
+        cls.parent.states['required'] = Or(
+            cls.parent.states.get('required', False),
             Eval('account_parent', False) | Eval('taxes_parent', False))
-        self.parent.depends = copy.copy(self.parent.depends)
-        self.parent.depends.extend(['account_parent', 'taxes_parent'])
-        self._reset_columns()
+        cls.parent.depends.extend(['account_parent', 'taxes_parent'])
 
-    def get_account(self, ids, name):
-        accounts = {}
-        for category in self.browse(ids):
-            if category.account_parent:
-                accounts[category.id] = category.parent[name].id
-            elif category[name[:-5]]:
-                accounts[category.id] = category[name[:-5]].id
-            else:
-                self.raise_user_error('missing_account',
-                    (category.name, category.id))
-        return accounts
+    def get_account(self, name):
+        if self.account_parent:
+            return getattr(self.parent, name).id
+        elif getattr(self, name[:-5]):
+            return getattr(self, name[:-5]).id
+        else:
+            self.raise_user_error('missing_account', (self.name, self.id))
 
-    def get_taxes(self, ids, name):
-        taxes = {}
-        for category in self.browse(ids):
-            if category.taxes_parent:
-                taxes[category.id] = [x.id for x in category.parent[name]]
-            else:
-                taxes[category.id] = [x.id for x in category[name[:-5]]]
-        return taxes
+    def get_taxes(self, name):
+        if self.taxes_parent:
+            return [x.id for x in getattr(self.parent, name)]
+        else:
+            return [x.id for x in getattr(self, name[:-5])]
 
-    def on_change_account_expense(self, values):
-        account_obj = Pool().get('account.account')
+    def on_change_account_expense(self):
         supplier_taxes = []
         result = {
             'supplier_taxes': supplier_taxes,
-        }
-        if values.get('account_expense'):
-            account = account_obj.browse(values['account_expense'])
-            supplier_taxes.extend(tax.id for tax in account.taxes)
+            }
+        if self.account_expense:
+            supplier_taxes.extend(tax.id for tax in self.account_expense.taxes)
         return result
 
-    def on_change_account_revenue(self, values):
-        account_obj = Pool().get('account.account')
+    def on_change_account_revenue(self):
         customer_taxes = []
         result = {
             'customer_taxes': customer_taxes,
-        }
-        if values.get('account_revenue'):
-            account = account_obj.browse(values['account_revenue'])
-            customer_taxes.extend(tax.id for tax in account.taxes)
+            }
+        if self.account_revenue:
+            customer_taxes.extend(tax.id for tax in self.account_revenue.taxes)
         return result
 
-Category()
-
 
 class CategoryCustomerTax(ModelSQL):
     'Category - Customer Tax'
-    _name = 'product.category-customer-account.tax'
+    __name__ = 'product.category-customer-account.tax'
     _table = 'product_category_customer_taxes_rel'
-    _description = __doc__
     category = fields.Many2One('product.category', 'Category',
             ondelete='CASCADE', select=True, required=True)
     tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
             required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.6 product renamed into category
-        table = TableHandler(cursor, self)
+        table = TableHandler(cursor, cls)
         if table.column_exist('product'):
             table.index_action('product', action='remove')
             table.drop_fk('product')
             table.column_rename('product', 'category')
-        super(CategoryCustomerTax, self).init(module_name)
-
-CategoryCustomerTax()
+        super(CategoryCustomerTax, cls).__register__(module_name)
 
 
 class CategorySupplierTax(ModelSQL):
     'Category - Supplier Tax'
-    _name = 'product.category-supplier-account.tax'
+    __name__ = 'product.category-supplier-account.tax'
     _table = 'product_category_supplier_taxes_rel'
-    _description = __doc__
     category = fields.Many2One('product.category', 'Category',
             ondelete='CASCADE', select=True, required=True)
     tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
             required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.6 product renamed into category
-        table = TableHandler(cursor, self)
+        table = TableHandler(cursor, cls)
         if table.column_exist('product'):
             table.index_action('product', action='remove')
             table.drop_fk('product')
             table.column_rename('product', 'category')
-        super(CategorySupplierTax, self).init(module_name)
+        super(CategorySupplierTax, cls).__register__(module_name)
 
-CategorySupplierTax()
 
-
-class Template(ModelSQL, ModelView):
-    _name = 'product.template'
-
+class Template:
+    __name__ = 'product.template'
     account_category = fields.Boolean('Use Category\'s accounts',
             help='Use the accounts defined on the category')
     account_expense = fields.Property(fields.Many2One('account.account',
@@ -215,92 +196,71 @@
     supplier_taxes_used = fields.Function(fields.One2Many('account.tax', None,
         'Supplier Taxes Used'), 'get_taxes')
 
-    def __init__(self):
-        super(Template, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Template, cls).__setup__()
+        cls._error_messages.update({
             'missing_account': 'There is no account ' \
                     'expense/revenue defined on the product ' \
                     '%s (%d)',
             })
-        self.category = copy.copy(self.category)
-        self.category.states = copy.copy(self.category.states)
-        self.category.states['required'] = Or(
-            self.category.states.get('required', False),
+        cls.category.states['required'] = Or(
+            cls.category.states.get('required', False),
             Eval('account_category', False) | Eval('taxes_category', False))
-        self.category.depends = copy.copy(self.category.depends)
-        self.category.depends.extend(['account_category', 'taxes_category'])
-        self._reset_columns()
+        cls.category.depends.extend(['account_category', 'taxes_category'])
 
-    def default_taxes_category(self):
+    @staticmethod
+    def default_taxes_category():
         return None
 
-    def get_account(self, ids, name):
-        accounts = {}
-        for product in self.browse(ids):
-            if product.account_category:
-                accounts[product.id] = product.category[name].id
-            elif product[name[:-5]]:
-                accounts[product.id] = product[name[:-5]].id
-            else:
-                self.raise_user_error('missing_account',
-                    (product.name, product.id))
-        return accounts
+    def get_account(self, name):
+        if self.account_category:
+            return getattr(self.category, name).id
+        elif getattr(self, name[:-5]):
+            return getattr(self, name[:-5]).id
+        else:
+            self.raise_user_error('missing_account', (self.name, self.id))
 
-    def get_taxes(self, ids, name):
-        taxes = {}
-        for product in self.browse(ids):
-            if product.taxes_category:
-                taxes[product.id] = [x.id for x in product.category[name]]
-            else:
-                taxes[product.id] = [x.id for x in product[name[:-5]]]
-        return taxes
+    def get_taxes(self, name):
+        if self.taxes_category:
+            return [x.id for x in getattr(self.category, name)]
+        else:
+            return [x.id for x in getattr(self, name[:-5])]
 
-    def on_change_account_expense(self, values):
-        account_obj = Pool().get('account.account')
+    def on_change_account_expense(self):
         supplier_taxes = []
         result = {
             'supplier_taxes': supplier_taxes,
-        }
-        if values.get('account_expense'):
-            account = account_obj.browse(values['account_expense'])
-            supplier_taxes.extend(tax.id for tax in account.taxes)
+            }
+        if self.account_expense:
+            supplier_taxes.extend(tax.id for tax in self.account_expense.taxes)
         return result
 
-    def on_change_account_revenue(self, values):
-        account_obj = Pool().get('account.account')
+    def on_change_account_revenue(self):
         customer_taxes = []
         result = {
             'customer_taxes': customer_taxes,
-        }
-        if values.get('account_revenue'):
-            account = account_obj.browse(values['account_revenue'])
-            customer_taxes.extend(tax.id for tax in account.taxes)
+            }
+        if self.account_revenue:
+            customer_taxes.extend(tax.id for tax in self.account_revenue.taxes)
         return result
 
-Template()
-
 
 class TemplateCustomerTax(ModelSQL):
     'Product Template - Customer Tax'
-    _name = 'product.template-customer-account.tax'
+    __name__ = 'product.template-customer-account.tax'
     _table = 'product_customer_taxes_rel'
-    _description = __doc__
     product = fields.Many2One('product.template', 'Product Template',
             ondelete='CASCADE', select=True, required=True)
     tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
             required=True)
 
-TemplateCustomerTax()
-
 
 class TemplateSupplierTax(ModelSQL):
     'Product Template - Supplier Tax'
-    _name = 'product.template-supplier-account.tax'
+    __name__ = 'product.template-supplier-account.tax'
     _table = 'product_supplier_taxes_rel'
-    _description = __doc__
     product = fields.Many2One('product.template', 'Product Template',
             ondelete='CASCADE', select=True, required=True)
     tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
             required=True)
-
-TemplateSupplierTax()
diff -r 41a25660ece8 trytond/modules/account_statement/__init__.py
--- a/trytond/modules/account_statement/__init__.py	Wed Sep 19 22:19:04 2012 +0200
+++ b/trytond/modules/account_statement/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,5 +1,14 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .journal import *
 from .statement import *
+
+
+def register():
+    Pool.register(
+        Journal,
+        Statement,
+        Line,
+        module='account_statement', type_='model')
diff -r 41a25660ece8 trytond/modules/account_statement/journal.py
--- a/trytond/modules/account_statement/journal.py	Wed Sep 19 22:19:04 2012 +0200
+++ b/trytond/modules/account_statement/journal.py	Mon Sep 24 20:36:39 2012 +0200
@@ -4,12 +4,12 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Journal']
+
 
 class Journal(ModelSQL, ModelView):
     'Statement Journal'
-    _name = 'account.statement.journal'
-    _description = __doc__
-
+    __name__ = 'account.statement.journal'
     name = fields.Char('Name', required=True)
     journal = fields.Many2One('account.journal', 'Journal', required=True,
         domain=[('type', '=', 'statement')])
@@ -17,13 +17,13 @@
     company = fields.Many2One('company.company', 'Company', required=True,
             select=True)
 
-    def default_currency(self):
+    @staticmethod
+    def default_currency():
         if Transaction().context.get('company'):
-            company_obj = Pool().get('company.company')
-            company = company_obj.browse(Transaction().context['company'])
+            Company = Pool().get('company.company')
+            company = Company(Transaction().context['company'])
             return company.currency.id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
-
-Journal()
diff -r 41a25660ece8 trytond/modules/account_statement/statement.py
--- a/trytond/modules/account_statement/statement.py	Wed Sep 19 22:19:04 2012 +0200
+++ b/trytond/modules/account_statement/statement.py	Mon Sep 24 20:36:39 2012 +0200
@@ -7,15 +7,15 @@
 from trytond.backend import TableHandler
 from trytond.pool import Pool
 
+__all__ = ['Statement', 'Line']
+
 _STATES = {'readonly': Eval('state') != 'draft'}
 _DEPENDS = ['state']
 
 
 class Statement(Workflow, ModelSQL, ModelView):
     'Account Statement'
-    _name = 'account.statement'
-    _description = __doc__
-
+    __name__ = 'account.statement'
     company = fields.Many2One('company.company', 'Company', required=True,
         select=True, states=_STATES, domain=[
             ('id', If(Eval('context', {}).contains('company'), '=', '!='),
@@ -33,7 +33,7 @@
         on_change=['journal', 'state', 'lines'], select=True,
         depends=['state', 'lines'])
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['journal']), 'get_currency_digits')
+            on_change_with=['journal']), 'on_change_with_currency_digits')
     date = fields.Date('Date', required=True, states=_STATES, depends=_DEPENDS,
         select=True)
     start_balance = fields.Numeric('Start Balance', required=True,
@@ -47,7 +47,7 @@
             digits=(16, Eval('currency_digits', 2)),
             on_change_with=['start_balance', 'end_balance'],
             depends=['currency_digits']),
-        'get_balance')
+        'on_change_with_balance')
     lines = fields.One2Many('account.statement.line', 'statement',
         'Transactions', states={
             'readonly': (Eval('state') != 'draft') | ~Eval('journal'),
@@ -62,21 +62,22 @@
     move_lines = fields.Function(fields.One2Many('account.move.line',
         None, 'Move Lines'), 'get_move_lines')
 
-    def __init__(self):
-        super(Statement, self).__init__()
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Statement, cls).__setup__()
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
                 'wrong_end_balance': 'End Balance must be %s!',
                 'delete_cancel': 'Statement "%s" must be cancelled before ' \
                     'deletion!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('draft', 'validated'),
                 ('validated', 'posted'),
                 ('validated', 'cancel'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'draft': {
                     'invisible': Eval('state') != 'cancel',
                     },
@@ -91,259 +92,245 @@
                     },
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
 
         # Migration from 1.8: new field company
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         company_exist = table.column_exist('company')
 
-        super(Statement, self).init(module_name)
+        super(Statement, cls).__register__(module_name)
 
         # Migration from 1.8: fill new field company
         if not company_exist:
             offset = 0
             limit = cursor.IN_MAX
-            statement_ids = True
-            while statement_ids:
-                statement_ids = self.search([], offset=offset, limit=limit)
+            statements = True
+            while statements:
+                statements = cls.search([], offset=offset, limit=limit)
                 offset += limit
-                for statement in self.browse(statement_ids):
-                    self.write(statement.id, {
-                        'company': statement.journal.company.id,
-                    })
-            table = TableHandler(cursor, self, module_name)
+                for statement in statements:
+                    cls.write([statement], {
+                            'company': statement.journal.company.id,
+                            })
+            table = TableHandler(cursor, cls, module_name)
             table.not_null_action('company', action='add')
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
-    def default_currency_digits(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency_digits():
+        Company = Pool().get('company.company')
         if Transaction().context.get('company'):
-            company = company_obj.browse(Transaction().context['company'])
+            company = Company(Transaction().context['company'])
             return company.currency.digits
         return 2
 
-    def on_change_journal(self, value):
+    def on_change_journal(self):
         res = {}
-        if not value.get('journal'):
+        if not self.journal:
             return res
 
-        statement_ids = self.search([
-            ('journal', '=', value['journal']),
-            ], order=[
+        statements = self.search([
+                ('journal', '=', self.journal.id),
+                ], order=[
                 ('date', 'DESC'),
-            ], limit=1)
-        if not statement_ids:
+                ], limit=1)
+        if not statements:
             return res
 
-        statement = self.browse(statement_ids[0])
+        statement, = statements
         res['start_balance'] = statement.end_balance
         return res
 
-    def on_change_with_currency_digits(self, vals):
-        journal_obj = Pool().get('account.statement.journal')
-        if vals.get('journal'):
-            journal = journal_obj.browse(vals['journal'])
-            return journal.currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.journal:
+            return self.journal.currency.digits
         return 2
 
-    def get_currency_digits(self, ids, name):
+    @classmethod
+    def get_rec_name(cls, statements, name):
+        Lang = Pool().get('ir.lang')
+
+        lang, = Lang.search([
+                ('code', '=', Transaction().language),
+                ])
+
         res = {}
-        for statement in self.browse(ids):
-            res[statement.id] = statement.journal.currency.digits
+        for statement in statements:
+            res[statement.id] = (statement.journal.name + ' '
+                + Lang.currency(lang, statement.start_balance,
+                    statement.journal.currency, symbol=False, grouping=True)
+                + Lang.currency(lang, statement.end_balance,
+                    statement.journal.currency, symbol=False, grouping=True))
         return res
 
-    def get_rec_name(self, ids, name):
-        lang_obj = Pool().get('ir.lang')
-
-        if not ids:
-            return {}
-
-        lang_id, = lang_obj.search([
-                ('code', '=', Transaction().language),
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        statements = cls.search(['OR',
+                ('start_balance',) + tuple(clause[1:]),
+                ('end_balance',) + tuple(clause[1:]),
                 ])
-        lang = lang_obj.browse(lang_id)
-
-        res = {}
-        for statement in self.browse(ids):
-            res[statement.id] = statement.journal.name + ' ' + \
-                    lang.currency(lang, statement.start_balance,
-                        statement.journal.currency, symbol=False,
-                        grouping=True) + \
-                    lang.currency(lang, statement.end_balance,
-                        statement.journal.currency, symbol=False,
-                        grouping=True)
-        return res
-
-    def search_rec_name(self, name, clause):
-        ids = self.search(['OR',
-            ('start_balance',) + tuple(clause[1:]),
-            ('end_balance',) + tuple(clause[1:]),
-            ])
-        if ids:
-            return [('id', 'in', ids)]
+        if statements:
+            return [('id', 'in', [s.id for s in statements])]
         return [('journal',) + tuple(clause[1:])]
 
-    def get_move_lines(self, ids, name):
+    def get_move_lines(self, name):
         '''
-        Return the move lines that have been generated by the statements.
+        Return the move lines that have been generated by the statement.
         '''
-        res = {}
-        for statement in self.browse(ids):
-            res[statement.id] = []
-            for line in statement.lines:
-                if not line.move:
-                    continue
-                for move_line in line.move.lines:
-                    res[statement.id].append(move_line.id)
-        return res
+        move_lines = []
+        for line in self.lines:
+            if not line.move:
+                continue
+            for move_line in line.move.lines:
+                move_lines.append(move_line.id)
+        return move_lines
 
-    def get_end_balance(self, ids, name):
-        statements = self.browse(ids)
-        res = {}
-        for statement in statements:
-            res[statement.id] = statement.start_balance
-            for line in statement.lines:
-                res[statement.id] += line.amount
-        return res
+    def get_end_balance(self, name):
+        end_balance = self.start_balance
+        for line in self.lines:
+            end_balance += line.amount
+        return end_balance
 
-    def get_balance(self, ids, name):
-        return dict((s.id, s.end_balance - s.start_balance)
-            for s in self.browse(ids))
+    def on_change_with_balance(self, name=None):
+        return ((getattr(self, 'end_balance', 0) or 0)
+            - (getattr(self, 'start_balance', 0) or 0))
 
-    def on_change_with_balance(self, values):
-        if not set(('end_balance', 'start_balance')) <= set(values):
-            return Decimal(0)
-        else:
-            return Decimal(values.get('end_balance') or 0
-                - values.get('start_balance') or 0)
-
-    def on_change_lines(self, values):
+    def on_change_lines(self):
         pool = Pool()
-        invoice_obj = pool.get('account.invoice')
-        journal_obj = pool.get('account.statement.journal')
-        currency_obj = pool.get('currency.currency')
+        Currency = pool.get('currency.currency')
+        Line = pool.get('account.statement.line')
         res = {
             'lines': {},
-        }
-        if values.get('journal') and values.get('lines'):
-            journal = journal_obj.browse(values['journal'])
-            invoice_ids = set()
-            for line in values['lines']:
-                if line['invoice']:
-                    invoice_ids.add(line['invoice'])
+            }
+        if self.journal and self.lines:
+            invoices = set()
+            for line in self.lines:
+                if line.invoice:
+                    invoices.add(line.invoice)
             invoice_id2amount_to_pay = {}
-            for invoice in invoice_obj.browse(list(invoice_ids)):
+            for invoice in invoices:
                 with Transaction().set_context(date=invoice.currency_date):
                     invoice_id2amount_to_pay[invoice.id] = (
-                        currency_obj.compute(invoice.currency.id,
-                            invoice.amount_to_pay, journal.currency.id))
+                        Currency.compute(invoice.currency,
+                            invoice.amount_to_pay, self.journal.currency))
 
-            for line in values['lines']:
-                if line['invoice'] and line['id']:
-                    amount_to_pay = invoice_id2amount_to_pay[line['invoice']]
-                    if abs(line['amount']) > amount_to_pay:
+            for line in self.lines or []:
+                if line.invoice and line.id:
+                    amount_to_pay = invoice_id2amount_to_pay[line.invoice.id]
+                    if abs(line.amount) > amount_to_pay:
                         res['lines'].setdefault('update', [])
-                        if currency_obj.is_zero(journal.currency,
-                                amount_to_pay):
+                        if self.journal.currency.is_zero(amount_to_pay):
                             res['lines']['update'].append({
-                                'id': line['id'],
+                                'id': line.id,
                                 'invoice': None,
                                 })
                         else:
                             res['lines']['update'].append({
-                                'id': line['id'],
+                                'id': line.id,
                                 'amount': (amount_to_pay
-                                        if line['amount'] >= 0
+                                        if line.amount >= 0
                                         else -amount_to_pay),
                                 })
                             res['lines'].setdefault('add', [])
-                            vals = line.copy()
+                            vals = {}
+                            for field_name, field in Line._fields.iteritems():
+                                try:
+                                    value = getattr(line, field_name)
+                                except AttributeError:
+                                    continue
+                                if (value and field._type in ('many2one',
+                                            'one2one')):
+                                    vals[field_name] = value.id
+                                    vals[field_name + '.rec_name'] = \
+                                        value.rec_name
+                                else:
+                                    vals[field_name] = value
                             del vals['id']
-                            vals['amount'] = (abs(line['amount'])
+                            vals['amount'] = (abs(line.amount)
                                 - amount_to_pay)
-                            if line['amount'] < 0:
+                            if line.amount < 0:
                                 vals['amount'] = - vals['amount']
                             vals['invoice'] = None
+                            del vals['invoice.rec_name']
                             res['lines']['add'].append(vals)
-                    invoice_id2amount_to_pay[line['invoice']] = \
-                            amount_to_pay - abs(line['amount'])
+                    invoice_id2amount_to_pay[line.invoice.id] = \
+                            amount_to_pay - abs(line.amount)
         return res
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, statements):
         # Cancel before delete
-        self.cancel(ids)
-        for statement in self.browse(ids):
+        cls.cancel(statements)
+        for statement in statements:
             if statement.state != 'cancel':
-                self.raise_user_error('delete_cancel', statement.rec_name)
-        return super(Statement, self).delete(ids)
+                cls.raise_user_error('delete_cancel', statement.rec_name)
+        super(Statement, cls).delete(statements)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
+    def draft(cls, statements):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('validated')
-    def validate(self, ids):
-        statement_line_obj = Pool().get('account.statement.line')
-        lang_obj = Pool().get('ir.lang')
+    def validate(cls, statements):
+        Lang = Pool().get('ir.lang')
 
-        for statement in self.browse(ids):
+        for statement in statements:
             computed_end_balance = statement.start_balance
             for line in statement.lines:
                 computed_end_balance += line.amount
             if computed_end_balance != statement.end_balance:
-                lang_id, = lang_obj.search([
+                lang, = Lang.search([
                         ('code', '=', Transaction().language),
                         ])
-                lang = lang_obj.browse(lang_id)
 
-                amount = lang_obj.format(lang,
+                amount = Lang.format(lang,
                         '%.' + str(statement.journal.currency.digits) + 'f',
                         computed_end_balance, True)
-                self.raise_user_error('wrong_end_balance',
+                cls.raise_user_error('wrong_end_balance',
                     error_args=(amount,))
             for line in statement.lines:
-                statement_line_obj.create_move(line)
+                line.create_move()
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('posted')
-    def post(self, ids):
-        statement_line_obj = Pool().get('account.statement.line')
+    def post(cls, statements):
+        StatementLine = Pool().get('account.statement.line')
 
-        statements = self.browse(ids)
         lines = [l for s in statements for l in s.lines]
-        statement_line_obj.post_move(lines)
+        StatementLine.post_move(lines)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        statement_line_obj = Pool().get('account.statement.line')
+    def cancel(cls, statements):
+        StatementLine = Pool().get('account.statement.line')
 
-        statements = self.browse(ids)
         lines = [l for s in statements for l in s.lines]
-        statement_line_obj.delete_move(lines)
-
-Statement()
+        StatementLine.delete_move(lines)
 
 
 class Line(ModelSQL, ModelView):
     'Account Statement Line'
-    _name = 'account.statement.line'
-    _description = __doc__
-
+    __name__ = 'account.statement.line'
     statement = fields.Many2One('account.statement', 'Statement',
             required=True, ondelete='CASCADE')
     date = fields.Date('Date', required=True)
@@ -377,9 +364,10 @@
             },
         depends=['party', 'account', 'amount'])
 
-    def __init__(self):
-        super(Line, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Line, cls).__setup__()
+        cls._error_messages.update({
             'debit_credit_account_statement_journal': 'Please provide debit '
                 'and credit account on statement journal.',
             'same_debit_credit_account': 'Credit or debit account on '
@@ -387,222 +375,203 @@
             'amount_greater_invoice_amount_to_pay': 'Amount (%s) greater than '
                 'the amount to pay of invoice!',
             })
-        self._sql_constraints += [
+        cls._sql_constraints += [
             ('check_statement_line_amount', 'CHECK(amount != 0)',
                 'Amount should be a positive or negative value!'),
             ]
 
-    def default_amount(self):
+    @staticmethod
+    def default_amount():
         return Decimal(0)
 
-    def on_change_party(self, value):
-        party_obj = Pool().get('party.party')
-        invoice_obj = Pool().get('account.invoice')
+    def on_change_party(self):
         res = {}
 
-        if value.get('party'):
-            party = party_obj.browse(value['party'])
-            if value.get('amount'):
-                if value['amount'] > Decimal("0.0"):
-                    account = party.account_receivable
+        if self.party:
+            if self.amount:
+                if self.amount > Decimal("0.0"):
+                    account = self.party.account_receivable
                 else:
-                    account = party.account_payable
+                    account = self.party.account_payable
                 res['account'] = account.id
                 res['account.rec_name'] = account.rec_name
 
-        if value.get('invoice'):
-            if value.get('party'):
-                invoice = invoice_obj.browse(value['invoice'])
-                if invoice.party != value['party']:
+        if self.invoice:
+            if self.party:
+                if self.invoice.party != self.party:
                     res['invoice'] = None
             else:
                 res['invoice'] = None
         return res
 
-    def on_change_amount(self, value):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        invoice_obj = pool.get('account.invoice')
-        journal_obj = pool.get('account.statement.journal')
-        currency_obj = pool.get('currency.currency')
+    def on_change_amount(self):
+        Currency = Pool().get('currency.currency')
         res = {}
 
-        if value.get('party'):
-            party = party_obj.browse(value['party'])
-            if value.get('account') and value['account'] not in (
-                party.account_receivable.id, party.account_payable.id):
+        if self.party:
+            if self.account and self.account not in (
+                    self.party.account_receivable, self.party.account_payable):
                 # The user has entered a non-default value, we keep it.
                 pass
-            elif value.get('amount'):
-                if value['amount'] > Decimal("0.0"):
-                    account = party.account_receivable
+            elif self.amount:
+                if self.amount > Decimal("0.0"):
+                    account = self.party.account_receivable
                 else:
-                    account = party.account_payable
+                    account = self.party.account_payable
                 res['account'] = account.id
                 res['account.rec_name'] = account.rec_name
-        if value.get('invoice'):
-            if value.get('amount') and value.get('_parent_statement.journal'):
-                invoice = invoice_obj.browse(value['invoice'])
-                journal = journal_obj.browse(
-                    value['_parent_statement.journal'])
+        if self.invoice:
+            if self.amount and self.statement and self.statement.journal:
+                invoice = self.invoice
+                journal = self.statement.journal
                 with Transaction().set_context(date=invoice.currency_date):
-                    amount_to_pay = currency_obj.compute(invoice.currency.id,
-                        invoice.amount_to_pay, journal.currency.id)
-                if abs(value['amount']) > amount_to_pay:
+                    amount_to_pay = Currency.compute(invoice.currency,
+                        invoice.amount_to_pay, journal.currency)
+                if abs(self.amount) > amount_to_pay:
                     res['invoice'] = None
             else:
                 res['invoice'] = None
         return res
 
-    def on_change_account(self, value):
-        invoice_obj = Pool().get('account.invoice')
+    def on_change_account(self):
         res = {}
 
-        if value.get('invoice'):
-            if value.get('account'):
-                invoice = invoice_obj.browse(value['invoice'])
-                if invoice.account.id != value['account']:
+        if self.invoice:
+            if self.account:
+                if self.invoice.account != self.account:
                     res['invoice'] = None
             else:
                 res['invoice'] = None
         return res
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, lines, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default.setdefault('move', None)
         default.setdefault('invoice', None)
-        return super(Line, self).copy(ids, default=default)
+        return super(Line, cls).copy(lines, default=default)
 
-    def create_move(self, line):
+    def create_move(self):
         '''
-        Create move for the statement line and return move id if created.
+        Create move for the statement line and return move if created.
         '''
         pool = Pool()
-        move_obj = pool.get('account.move')
-        period_obj = pool.get('account.period')
-        invoice_obj = pool.get('account.invoice')
-        currency_obj = pool.get('currency.currency')
-        move_line_obj = pool.get('account.move.line')
-        lang_obj = pool.get('ir.lang')
+        Move = pool.get('account.move')
+        Period = pool.get('account.period')
+        Invoice = pool.get('account.invoice')
+        Currency = pool.get('currency.currency')
+        MoveLine = pool.get('account.move.line')
+        Lang = pool.get('ir.lang')
 
-        if line.move:
+        if self.move:
             return
 
-        period_id = period_obj.find(line.statement.company.id,
-                date=line.date)
+        period_id = Period.find(self.statement.company.id, date=self.date)
 
-        move_lines = self._get_move_lines(line)
-        move_id = move_obj.create({
-                'name': unicode(line.date),
-                'period': period_id,
-                'journal': line.statement.journal.journal.id,
-                'date': line.date,
-                'lines': [('create', x) for x in move_lines],
-             })
+        move_lines = self._get_move_lines()
+        move = Move(
+            name=unicode(self.date),
+            period=period_id,
+            journal=self.statement.journal.journal,
+            date=self.date,
+            lines=move_lines,
+            )
+        move.save()
 
-        self.write(line.id, {
-            'move': move_id,
-            })
+        self.write([self], {
+                'move': move.id,
+                })
 
-        if line.invoice:
-            with Transaction().set_context(date=line.invoice.currency_date):
-                amount_to_pay = currency_obj.compute(line.invoice.currency.id,
-                        line.invoice.amount_to_pay,
-                        line.statement.journal.currency.id)
-            if amount_to_pay < abs(line.amount):
-                lang_id, = lang_obj.search([
+        if self.invoice:
+            with Transaction().set_context(date=self.invoice.currency_date):
+                amount_to_pay = Currency.compute(self.invoice.currency,
+                    self.invoice.amount_to_pay,
+                    self.statement.journal.currency)
+            if amount_to_pay < abs(self.amount):
+                lang, = Lang.search([
                         ('code', '=', Transaction().language),
                         ])
-                lang = lang_obj.browse(lang_id)
 
-                amount = lang_obj.format(lang,
-                    '%.' + str(line.statement.journal.currency.digits) + 'f',
-                    line.amount, True)
+                amount = Lang.format(lang,
+                    '%.' + str(self.statement.journal.currency.digits) + 'f',
+                    self.amount, True)
                 self.raise_user_error('amount_greater_invoice_amount_to_pay',
                         error_args=(amount,))
 
-            with Transaction().set_context(date=line.invoice.currency_date):
-                amount = currency_obj.compute(
-                        line.statement.journal.currency.id, line.amount,
-                        line.statement.company.currency.id)
+            with Transaction().set_context(date=self.invoice.currency_date):
+                amount = Currency.compute(self.statement.journal.currency,
+                    self.amount, self.statement.company.currency)
 
-            reconcile_lines = invoice_obj.get_reconcile_lines_for_amount(
-                    line.invoice, abs(amount))
+            reconcile_lines = self.invoice.get_reconcile_lines_for_amount(
+                abs(amount))
 
-            move = move_obj.browse(move_id)
-            line_id = None
             for move_line in move.lines:
-                if move_line.account.id == line.invoice.account.id:
-                    line_id = move_line.id
-                    invoice_obj.write(line.invoice.id, {
-                        'payment_lines': [('add', line_id)],
-                        })
+                if move_line.account == self.invoice.account:
+                    Invoice.write([self.invoice], {
+                            'payment_lines': [('add', [move_line.id])],
+                            })
                     break
             if reconcile_lines[1] == Decimal('0.0'):
-                line_ids = reconcile_lines[0] + [line_id]
-                move_line_obj.reconcile(line_ids)
-        return move_id
+                lines = reconcile_lines[0] + [move_line]
+                MoveLine.reconcile(lines)
+        return move
 
-    def post_move(self, lines):
-        move_obj = Pool().get('account.move')
-        move_obj.post([l.move.id for l in lines if l.move])
+    @classmethod
+    def post_move(cls, lines):
+        Move = Pool().get('account.move')
+        Move.post([l.move for l in lines if l.move])
 
-    def delete_move(self, lines):
-        move_obj = Pool().get('account.move')
-        move_obj.delete([l.move.id for l in lines if l.move])
+    @classmethod
+    def delete_move(cls, lines):
+        Move = Pool().get('account.move')
+        Move.delete([l.move for l in lines if l.move])
 
-    def _get_move_lines(self, statement_line):
+    def _get_move_lines(self):
         '''
-        Return the values of the move lines for the statement line
-
-        :param statement_line: a BrowseRecord of the statement line
-        :return: a list of dictionary of move line values
+        Return the move lines for the statement line
         '''
-        currency_obj = Pool().get('currency.currency')
+        pool = Pool()
+        MoveLine = pool.get('account.move.line')
+        Currency = Pool().get('currency.currency')
         zero = Decimal("0.0")
-        amount = currency_obj.compute(
-            statement_line.statement.journal.currency, statement_line.amount,
-            statement_line.statement.company.currency)
-        if statement_line.statement.journal.currency.id != \
-                statement_line.statement.company.currency.id:
-            second_currency = statement_line.statement.journal.currency.id
-            amount_second_currency = abs(statement_line.amount)
+        amount = Currency.compute(self.statement.journal.currency, self.amount,
+            self.statement.company.currency)
+        if self.statement.journal.currency != self.statement.company.currency:
+            second_currency = self.statement.journal.currency.id
+            amount_second_currency = abs(self.amount)
         else:
             amount_second_currency = None
             second_currency = None
 
-        party_id = statement_line.party.id if statement_line.party else None
-        vals = []
-        vals.append({
-            'name': unicode(statement_line.date),
-            'debit': amount < zero and -amount or zero,
-            'credit': amount >= zero and amount or zero,
-            'account': statement_line.account.id,
-            'party': party_id,
-            'second_currency': second_currency,
-            'amount_second_currency': amount_second_currency,
-            })
+        move_lines = []
+        move_lines.append(MoveLine(
+                name=unicode(self.date),
+                debit=amount < zero and -amount or zero,
+                credit=amount >= zero and amount or zero,
+                account=self.account,
+                party=self.party,
+                second_currency=second_currency,
+                amount_second_currency=amount_second_currency,
+                ))
 
-        journal = statement_line.statement.journal.journal
-        if statement_line.amount >= zero:
+        journal = self.statement.journal.journal
+        if self.amount >= zero:
             account = journal.credit_account
         else:
             account = journal.debit_account
         if not account:
             self.raise_user_error('debit_credit_account_statement_journal')
-        if statement_line.account.id == account.id:
+        if self.account == account:
             self.raise_user_error('same_debit_credit_account')
-        vals.append({
-            'name': unicode(statement_line.date),
-            'debit': amount >= zero and amount or zero,
-            'credit': amount < zero and -amount or zero,
-            'account': account.id,
-            'party': party_id,
-            'second_currency': second_currency,
-            'amount_second_currency': amount_second_currency,
-            })
-        return vals
-
-Line()
+        move_lines.append(MoveLine(
+                name=unicode(self.date),
+                debit=amount >= zero and amount or zero,
+                credit=amount < zero and -amount or zero,
+                account=account,
+                party=self.party,
+                second_currency=second_currency,
+                amount_second_currency=amount_second_currency,
+                ))
+        return move_lines
diff -r 41ffec50a601 trytond/modules/account_stock_anglo_saxon/__init__.py
--- a/trytond/modules/account_stock_anglo_saxon/__init__.py	Tue Sep 11 15:28:27 2012 +0200
+++ b/trytond/modules/account_stock_anglo_saxon/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,6 +1,16 @@
 #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.pool import Pool
 from .product import *
 from .stock import *
 from .invoice import *
+
+
+def register():
+    Pool.register(
+        Category,
+        Template,
+        Move,
+        InvoiceLine,
+        module='account_stock_anglo_saxon', type_='model')
diff -r 41ffec50a601 trytond/modules/account_stock_anglo_saxon/invoice.py
--- a/trytond/modules/account_stock_anglo_saxon/invoice.py	Tue Sep 11 15:28:27 2012 +0200
+++ b/trytond/modules/account_stock_anglo_saxon/invoice.py	Mon Sep 24 20:36:39 2012 +0200
@@ -2,15 +2,17 @@
 #this repository contains the full copyright notices and license terms.
 from decimal import Decimal
 import operator
-from trytond.model import ModelView, ModelSQL
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 
+__all__ = ['InvoiceLine']
+__metaclass__ = PoolMeta
 
-class InvoiceLine(ModelSQL, ModelView):
-    _name = 'account.invoice.line'
 
-    def _get_anglo_saxon_move_lines(self, line, amount, type_):
+class InvoiceLine:
+    __name__ = 'account.invoice.line'
+
+    def _get_anglo_saxon_move_lines(self, amount, type_):
         '''
         Return account move for anglo-saxon stock accounting
         '''
@@ -19,7 +21,7 @@
 
         result = []
         move_line = {}
-        move_line['name'] = line.description
+        move_line['name'] = self.description
         move_line['amount_second_currency'] = Decimal('0.0')
         move_line['second_currency'] = None
 
@@ -31,7 +33,7 @@
             move_line['debit'] = Decimal('0.0')
             move_line['credit'] = amount
             account_type = type_[4:]
-        move_line['account'] = getattr(line.product,
+        move_line['account'] = getattr(self.product,
                 'account_stock_%s_used' % account_type).id
 
         result.append(move_line)
@@ -39,55 +41,50 @@
         move_line['debit'], move_line['credit'] = \
                 move_line['credit'], move_line['debit']
         if type_.endswith('supplier'):
-            move_line['account'] = line.account.id
+            move_line['account'] = self.account.id
         else:
-            move_line['account'] = line.product.account_cogs_used.id
+            move_line['account'] = self.product.account_cogs_used.id
         result.append(move_line)
         return result
 
-    def get_move_line(self, line):
-        pool = Pool()
-        move_obj = pool.get('stock.move')
-        currency_obj = pool.get('currency.currency')
+    def get_move_line(self):
+        Move = Pool().get('stock.move')
 
-        result = super(InvoiceLine, self).get_move_line(line)
+        result = super(InvoiceLine, self).get_move_line()
 
-        if line.type != 'line':
+        if self.type != 'line':
             return result
-        if not line.product:
+        if not self.product:
             return result
-        if line.product.type != 'goods':
+        if self.product.type != 'goods':
             return result
 
         moves = []
         # other types will get current cost price
-        if line.invoice.type == 'in_invoice':
-            moves = [move for purchase_line in line.purchase_lines
+        if self.invoice.type == 'in_invoice':
+            moves = [move for purchase_line in self.purchase_lines
                     for move in purchase_line.moves
                     if move.state == 'done']
-        elif line.invoice.type == 'out_invoice':
+        elif self.invoice.type == 'out_invoice':
             with Transaction().set_user(0, set_context=True):
-                sale_lines = self.browse(line.id).sale_lines
+                sale_lines = self.__class__(self.id).sale_lines
             moves = [move for sale_line in sale_lines
                     for move in sale_line.moves
                     if move.state == 'done']
-        if line.invoice.type == 'in_invoice':
+        if self.invoice.type == 'in_invoice':
             type_ = 'in_supplier'
-        elif line.invoice.type == 'out_invoice':
+        elif self.invoice.type == 'out_invoice':
             type_ = 'out_customer'
-        elif line.invoice.type == 'in_credit_note':
+        elif self.invoice.type == 'in_credit_note':
             type_ = 'out_supplier'
-        elif line.invoice.type == 'out_credit_note':
+        elif self.invoice.type == 'out_credit_note':
             type_ = 'in_customer'
 
         moves.sort(key=operator.attrgetter('effective_date'))
-        cost = move_obj.update_anglo_saxon_quantity_product_cost(
-                line.product, moves, line.quantity, line.unit, type_)
-        cost = currency_obj.round(line.invoice.currency, cost)
+        cost = Move.update_anglo_saxon_quantity_product_cost(
+            self.product, moves, self.quantity, self.unit, type_)
+        cost = self.invoice.currency.round(cost)
 
-        anglo_saxon_move_lines = self._get_anglo_saxon_move_lines(line, cost,
-                type_)
+        anglo_saxon_move_lines = self._get_anglo_saxon_move_lines(cost, type_)
         result.extend(anglo_saxon_move_lines)
         return result
-
-InvoiceLine()
diff -r 41ffec50a601 trytond/modules/account_stock_anglo_saxon/product.py
--- a/trytond/modules/account_stock_anglo_saxon/product.py	Tue Sep 11 15:28:27 2012 +0200
+++ b/trytond/modules/account_stock_anglo_saxon/product.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,12 +1,15 @@
 #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 ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 
+__all__ = ['Category', 'Template']
+__metaclass__ = PoolMeta
 
-class Category(ModelSQL, ModelView):
-    _name = 'product.category'
 
+class Category:
+    __name__ = 'product.category'
     account_cogs = fields.Property(fields.Many2One('account.account',
             'Account Cost of Goods Sold', domain=[
                 ('kind', '!=', 'view'),
@@ -20,12 +23,9 @@
     account_cogs_used = fields.Function(fields.Many2One('account.account',
         'Account Cost of Goods Sold Used'), 'get_account')
 
-Category()
 
-
-class Template(ModelSQL, ModelView):
-    _name = 'product.template'
-
+class Template:
+    __name__ = 'product.template'
     account_cogs = fields.Property(fields.Many2One('account.account',
             'Account Cost of Goods Sold', domain=[
                 ('kind', '!=', 'view'),
@@ -44,5 +44,3 @@
             depends=['account_category', 'type']))
     account_cogs_used = fields.Function(fields.Many2One('account.account',
         'Account Cost of Goods Sold Used'), 'get_account')
-
-Template()
diff -r 41ffec50a601 trytond/modules/account_stock_anglo_saxon/stock.py
--- a/trytond/modules/account_stock_anglo_saxon/stock.py	Tue Sep 11 15:28:27 2012 +0200
+++ b/trytond/modules/account_stock_anglo_saxon/stock.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,75 +1,82 @@
 #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 trytond.model import ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Move']
+__metaclass__ = PoolMeta
 
-class Move(ModelSQL, ModelView):
-    _name = 'stock.move'
+
+class Move:
+    __name__ = 'stock.move'
     anglo_saxon_quantity = fields.Float('Anglo-Saxon Quantity', required=True,
         digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
 
-    def __init__(self):
-        super(Move, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Move, cls).__setup__()
+        cls._sql_constraints += [
             ('check_anglo_saxon_quantity',
                 'CHECK(quantity >= anglo_saxon_quantity)',
                 'Anglo-Saxon quantity can not be greater than quantity!'),
-        ]
+            ]
 
-    def default_anglo_saxon_quantity(self):
+    @staticmethod
+    def default_anglo_saxon_quantity():
         return 0.0
 
-    def _get_account_stock_move_lines(self, move, type_):
-        uom_obj = Pool().get('product.uom')
-        currency_obj = Pool().get('currency.currency')
-        lines = super(Move, self)._get_account_stock_move_lines(move, type_)
+    def _get_account_stock_move_lines(self, type_):
+        pool = Pool()
+        Uom = pool.get('product.uom')
+        AccountMoveLine = pool.get('account.move.line')
+        lines = super(Move, self)._get_account_stock_move_lines(type_)
         if (type_.endswith('supplier')
-                and move.product.cost_price_method == 'fixed'):
-            cost_price = uom_obj.compute_price(move.product.default_uom,
-                move.cost_price, move.uom)
-            amount = currency_obj.round(move.company.currency,
-                Decimal(str(move.quantity)) * (move.unit_price - cost_price))
-            if currency_obj.is_zero(move.company.currency, amount):
+                and self.product.cost_price_method == 'fixed'):
+            cost_price = Uom.compute_price(self.product.default_uom,
+                self.cost_price, self.uom)
+            amount = self.company.currency.round(
+                Decimal(str(self.quantity)) * (self.unit_price - cost_price))
+            if self.company.currency.is_zero(amount):
                 return lines
-            account_id = move.product.account_stock_supplier_used.id
+            account = self.product.account_stock_supplier_used
             for move_line in lines:
-                if move_line['account'] == account_id:
+                if move_line.account == account:
                     break
             else:
                 return lines
             if type_.startswith('in_'):
-                move_line['credit'] += amount
+                move_line.credit += amount
                 debit = amount
                 credit = Decimal(0)
             else:
-                move_line['debit'] += amount
+                move_line.debit += amount
                 debit = Decimal(0)
                 credit = amount
             if amount < Decimal(0):
                 debit, credit = -credit, -debit
-            move_line = {
-                'name': move.rec_name,
-                'debit': debit,
-                'credit': credit,
-                'account': move.product.account_expense_used.id,
-                }
+            move_line = AccountMoveLine(
+                name=self.rec_name,
+                debit=debit,
+                credit=credit,
+                account=self.product.account_expense_used,
+                )
             lines.append(move_line)
         return lines
 
-    def _get_anglo_saxon_move(self, moves, quantity):
+    @classmethod
+    def _get_anglo_saxon_move(cls, moves, quantity):
         '''
         Generator of (move, qty) where move is the move to be consumed and qty
         is the quantity (in the product default uom) to be consumed on this
         move.
         '''
-        uom_obj = Pool().get('product.uom')
+        Uom = Pool().get('product.uom')
 
         consumed_qty = 0.0
         for move in moves:
-            qty = uom_obj.compute_qty(move.uom,
+            qty = Uom.compute_qty(move.uom,
                     move.quantity - move.anglo_saxon_quantity,
                     move.product.default_uom, round=False)
             if qty <= 0.0:
@@ -80,35 +87,36 @@
                 break
             yield (move, qty)
 
-    def update_anglo_saxon_quantity_product_cost(self, product, moves,
+    @classmethod
+    def update_anglo_saxon_quantity_product_cost(cls, product, moves,
             quantity, uom, type_):
         '''
         Return the cost for quantity based on lines.
         Update anglo_saxon_quantity on the concerned moves.
         '''
-        uom_obj = Pool().get('product.uom')
+        Uom = Pool().get('product.uom')
 
         for move in moves:
             assert move.product == product, 'wrong product'
         assert type_.startswith('in_') or type_.startswith('out_'), \
             'wrong type'
 
-        total_qty = uom_obj.compute_qty(uom, quantity, product.default_uom,
+        total_qty = Uom.compute_qty(uom, quantity, product.default_uom,
                 round=False)
 
         cost = Decimal('0.0')
         consumed_qty = 0.0
-        for move, move_qty in self._get_anglo_saxon_move(moves, total_qty):
+        for move, move_qty in cls._get_anglo_saxon_move(moves, total_qty):
             consumed_qty += move_qty
 
             if type_.startswith('in_'):
-                move_cost_price = uom_obj.compute_price(move.uom,
+                move_cost_price = Uom.compute_price(move.uom,
                         move.unit_price, move.product.default_uom)
             else:
                 move_cost_price = move.cost_price
             cost += move_cost_price * Decimal(str(move_qty))
 
-            self.write(move.id, {
+            cls.write([move], {
                 'anglo_saxon_quantity': ((move.anglo_saxon_quantity or 0.0)
                     + move_qty),
                 })
@@ -119,12 +127,11 @@
             cost += product.cost_price * Decimal(str(qty))
         return cost
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, moves, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default.setdefault('anglo_saxon_quantity',
-            self.default_anglo_saxon_quantity())
-        return super(Move, self).copy(ids, default=default)
-
-Move()
+            cls.default_anglo_saxon_quantity())
+        return super(Move, cls).copy(moves, default=default)
diff -r 41ffec50a601 trytond/modules/account_stock_anglo_saxon/tests/scenario_account_stock_anglo_saxon.rst
--- a/trytond/modules/account_stock_anglo_saxon/tests/scenario_account_stock_anglo_saxon.rst	Tue Sep 11 15:28:27 2012 +0200
+++ b/trytond/modules/account_stock_anglo_saxon/tests/scenario_account_stock_anglo_saxon.rst	Mon Sep 24 20:36:39 2012 +0200
@@ -165,7 +165,7 @@
     >>> product.account_journal_stock_customer = stock_journal
     >>> product.account_journal_stock_lost_found = stock_journal
     >>> product.save()
-    >>> product_average = Product(Product.copy(product.id, config.context))
+    >>> product_average = Product(Product.copy([product.id], config.context)[0])
     >>> product_average.cost_price_method = 'average'
     >>> product_average.save()
 
diff -r 3ae2e9579dcd trytond/modules/account_stock_continental/__init__.py
--- a/trytond/modules/account_stock_continental/__init__.py	Mon Sep 24 12:12:46 2012 +0200
+++ b/trytond/modules/account_stock_continental/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,6 +1,21 @@
 #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.pool import Pool
 from .stock import *
 from .product import *
 from .account import *
+
+
+def register():
+    Pool.register(
+        Move,
+        Category,
+        Template,
+        Configuration,
+        UpdateCostPriceAsk,
+        UpdateCostPriceShowMove,
+        module='account_stock_continental', type_='model')
+    Pool.register(
+        UpdateCostPrice,
+        module='account_stock_continental', type_='wizard')
diff -r 3ae2e9579dcd trytond/modules/account_stock_continental/account.py
--- a/trytond/modules/account_stock_continental/account.py	Mon Sep 24 12:12:46 2012 +0200
+++ b/trytond/modules/account_stock_continental/account.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,12 +1,15 @@
 #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 Model, fields
+from trytond.model import fields
 from trytond.pyson import Eval, Bool, Get
+from trytond.pool import PoolMeta
 
+__all__ = ['Configuration']
+__metaclass__ = PoolMeta
 
-class Configuration(Model):
-    _name = 'account.configuration'
 
+class Configuration:
+    __name__ = 'account.configuration'
     stock_journal = fields.Property(fields.Many2One(
             'account.journal', 'Stock Journal',
             states={
@@ -16,5 +19,3 @@
             'account.account', 'Cost Price Counterpart Account', domain=[
                 ('company', 'in', [Get(Eval('context', {}), 'company'), None]),
                 ]))
-
-Configuration()
diff -r 3ae2e9579dcd trytond/modules/account_stock_continental/product.py
--- a/trytond/modules/account_stock_continental/product.py	Mon Sep 24 12:12:46 2012 +0200
+++ b/trytond/modules/account_stock_continental/product.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,17 +1,19 @@
 #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 copy
 from decimal import Decimal
-from trytond.model import ModelView, ModelSQL, fields
+from trytond.model import ModelView, fields
 from trytond.wizard import Wizard, StateView, StateTransition, Button
 from trytond.pyson import Eval, Get
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Category', 'Template', 'UpdateCostPriceAsk',
+    'UpdateCostPriceShowMove', 'UpdateCostPrice']
+__metaclass__ = PoolMeta
 
-class Category(ModelSQL, ModelView):
-    _name = 'product.category'
 
+class Category:
+    __name__ = 'product.category'
     account_stock = fields.Property(fields.Many2One('account.account',
             'Account Stock', domain=[
                 ('kind', '=', 'stock'),
@@ -61,12 +63,9 @@
     account_stock_lost_found_used = fields.Function(fields.Many2One(
         'account.account', 'Account Stock Lost and Found'), 'get_account')
 
-Category()
 
-
-class Template(ModelSQL, ModelView):
-    _name = 'product.template'
-
+class Template:
+    __name__ = 'product.template'
     account_stock = fields.Property(fields.Many2One('account.account',
             'Account Stock',
             domain=[
@@ -140,33 +139,26 @@
     account_stock_lost_found_used = fields.Function(fields.Many2One(
         'account.account', 'Account Stock Lost and Found'), 'get_account')
 
-    def __init__(self):
-        super(Template, self).__init__()
-        self.cost_price = copy.copy(self.cost_price)
-        self.cost_price.states = copy.copy(self.cost_price.states)
-        self.cost_price.states['readonly'] = (
-            self.cost_price.states.get('readonly', False)
+    @classmethod
+    def __setup__(cls):
+        super(Template, cls).__setup__()
+        cls.cost_price.states['readonly'] = (
+            cls.cost_price.states.get('readonly', False)
             | ((Eval('type', 'goods') == 'goods') & (Eval('id', -1) >= 0)))
-        self.cost_price.depends = copy.copy(self.cost_price.depends)
-        if 'type' not in self.cost_price.depends:
-            self.cost_price.depends.append('type')
-        self._reset_columns()
-
-Template()
+        if 'type' not in cls.cost_price.depends:
+            cls.cost_price.depends.append('type')
 
 
 class UpdateCostPriceAsk(ModelView):
     'Update Cost Price Ask'
-    _name = 'product.update_cost_price.ask'
+    __name__ = 'product.update_cost_price.ask'
     product = fields.Many2One('product.product', 'Product', readonly=True)
     cost_price = fields.Numeric('Cost Price', required=True, digits=(16, 4))
 
-UpdateCostPriceAsk()
-
 
 class UpdateCostPriceShowMove(ModelView):
     'Update Cost Price Show Move'
-    _name = 'product.update_cost_price.show_move'
+    __name__ = 'product.update_cost_price.show_move'
     price_difference = fields.Numeric('Price Difference', readonly=True,
         digits=(16, 4))
     amount = fields.Numeric('Amount', readonly=True,
@@ -183,12 +175,10 @@
         depends=['company', 'stock_account'], required=True)
     description = fields.Char('Description', required=True)
 
-UpdateCostPriceShowMove()
-
 
 class UpdateCostPrice(Wizard):
     'Update Cost Price'
-    _name = 'product.update_cost_price'
+    __name__ = 'product.update_cost_price'
     start_state = 'ask_price'
     ask_price = StateView('product.update_cost_price.ask',
         'account_stock_continental.update_cost_price_ask_form', [
@@ -204,116 +194,117 @@
     create_move = StateTransition()
     update_price = StateTransition()
 
-    def __init__(self):
-        super(UpdateCostPrice, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(UpdateCostPrice, cls).__setup__()
+        cls._error_messages.update({
                 'same_account': 'The stock account and the counterpart can '
                     'not be the same account',
                 })
 
-    def default_ask_price(self, session, fields):
+    def default_ask_price(self, fields):
         if 'product' in fields:
             return {
                 'product': Transaction().context['active_id'],
                 }
 
-    def get_quantity(self):
+    @staticmethod
+    def get_quantity():
         pool = Pool()
-        date_obj = pool.get('ir.date')
-        product_obj = pool.get('product.product')
-        stock_obj = pool.get('stock.location')
+        Date = pool.get('ir.date')
+        Product = pool.get('product.product')
+        Stock = pool.get('stock.location')
 
-        locations = stock_obj.search([('type', '=', 'storage')])
-        stock_date_end = date_obj.today()
-        with Transaction().set_context(locations=locations,
+        locations = Stock.search([('type', '=', 'storage')])
+        stock_date_end = Date.today()
+        with Transaction().set_context(locations=[l.id for l in locations],
                 stock_date_end=stock_date_end):
-            product = product_obj.browse(Transaction().context['active_id'])
-            if hasattr(product_obj, 'cost_price'):
+            product = Product(Transaction().context['active_id'])
+            if hasattr(Product, 'cost_price'):
                 return product.quantity
             else:
                 return product.template.quantity
 
-    def transition_should_show_move(self, session):
+    def transition_should_show_move(self):
         if self.get_quantity() != 0:
             return 'show_move'
         return 'update_price'
 
-    def default_show_move(self, session, fields):
+    def default_show_move(self, fields):
         pool = Pool()
-        currency_obj = pool.get('currency.currency')
-        user_obj = pool.get('res.user')
-        accountconf_obj = pool.get('account.configuration')
-        product_obj = pool.get('product.product')
+        User = pool.get('res.user')
+        AccountConfiguration = pool.get('account.configuration')
+        Product = pool.get('product.product')
 
-        product = product_obj.browse(Transaction().context['active_id'])
-        price_diff = (session.ask_price.cost_price
+        product = Product(Transaction().context['active_id'])
+        price_diff = (self.ask_price.cost_price
                 - product.cost_price)
-        user = user_obj.browse(Transaction().user)
-        amount = currency_obj.round(user.company.currency,
+        user = User(Transaction().user)
+        amount = user.company.currency.round(
             Decimal(str(self.get_quantity())) * price_diff)
-        stock_account = product.account_stock_used.id
-        config = accountconf_obj.browse(1)
-        stock_journal = config.stock_journal.id
-        counterpart = (config.cost_price_counterpart_account.id if
+        stock_account_id = product.account_stock_used.id
+        config = AccountConfiguration(1)
+        stock_journal_id = config.stock_journal.id
+        counterpart_id = (config.cost_price_counterpart_account.id if
             config.cost_price_counterpart_account else None)
         return {
-            'journal': stock_journal,
+            'journal': stock_journal_id,
             'amount': amount,
             'price_difference': price_diff,
-            'stock_account': stock_account,
-            'counterpart': counterpart,
+            'stock_account': stock_account_id,
+            'counterpart': counterpart_id,
             'currency_digits': user.company.currency.digits,
             }
 
-    def get_move_lines(self, session):
-        amount = session.show_move.amount
-        return [{
-                'name': session.ask_price.product.name,
-                'debit': amount if amount > 0 else 0,
-                'credit': -amount if amount < 0 else 0,
-                'account': session.show_move.stock_account.id,
-                },
-            {
-                'name': session.ask_price.product.name,
-                'debit': -amount if amount < 0 else 0,
-                'credit': amount if amount > 0 else 0,
-                'account': session.show_move.counterpart.id,
-                },
+    def get_move_lines(self):
+        Line = Pool.get('account.move.line')
+        amount = self.show_move.amount
+        return [Line(
+                name=self.ask_price.product.name,
+                debit=amount if amount > 0 else 0,
+                credit=-amount if amount < 0 else 0,
+                account=self.show_move.stock_account,
+                ),
+            Line(
+                name=self.ask_price.product.name,
+                debit=-amount if amount < 0 else 0,
+                credit=amount if amount > 0 else 0,
+                account=self.show_move.counterpart,
+                ),
             ]
 
-    def get_move(self, session):
+    def get_move(self):
         pool = Pool()
-        date_obj = pool.get('ir.date')
-        period_obj = pool.get('account.period')
-        user_obj = pool.get('res.user')
+        Date = pool.get('ir.date')
+        Period = pool.get('account.period')
+        User = pool.get('res.user')
+        Move = pool.get('account.move')
 
-        user = user_obj.browse(Transaction().user)
-        period_id = period_obj.find(user.company.id)
-        return {
-            'name': session.show_move.description,
-            'period': period_id,
-            'journal': session.show_move.journal.id,
-            'date': date_obj.today(),
-            'lines': [('create', l)
-                for l in self.get_move_lines(session)],
-            }
+        user = User(Transaction().user)
+        period_id = Period.find(user.company.id)
+        with Transaction().set_user(0, set_context=True):
+            return Move(
+                name=self.show_move.description,
+                period=period_id,
+                journal=self.show_move.journal,
+                date=Date.today(),
+                lines=self.get_move_lines(),
+                )
 
-    def transition_create_move(self, session):
-        move_obj = Pool().get('account.move')
+    def transition_create_move(self):
+        Move = Pool().get('account.move')
 
-        if session.show_move.counterpart == session.show_move.stock_account:
+        if self.show_move.counterpart == self.show_move.stock_account:
             self.raise_user_error('same_account')
-        move_data = self.get_move(session)
+        move = self.get_move()
+        move.save()
         with Transaction().set_user(0, set_context=True):
-            move_id = move_obj.create(move_data)
-            move_obj.post([move_id])
+            Move.post([move])
         return 'update_price'
 
-    def transition_update_price(self, session):
-        product_obj = Pool().get('product.product')
-        product_obj.write(session.ask_price.product.id, {
-                'cost_price': session.ask_price.cost_price,
+    def transition_update_price(self):
+        Product = Pool().get('product.product')
+        Product.write([self.ask_price.product], {
+                'cost_price': self.ask_price.cost_price,
                 })
         return 'end'
-
-UpdateCostPrice()
diff -r 3ae2e9579dcd trytond/modules/account_stock_continental/stock.py
--- a/trytond/modules/account_stock_continental/stock.py	Mon Sep 24 12:12:46 2012 +0200
+++ b/trytond/modules/account_stock_continental/stock.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,94 +1,102 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 from decimal import Decimal
-from trytond.model import Model, fields
-from trytond.pool import Pool
+
+from trytond.model import fields
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 
+__all__ = ['Move']
+__metaclass__ = PoolMeta
 
-class Move(Model):
-    _name = 'stock.move'
 
+class Move:
+    __name__ = 'stock.move'
     account_move = fields.Many2One('account.move', 'Account Move',
             readonly=True)
 
-    def _get_account_stock_move_lines(self, move, type_):
+    def _get_account_stock_move_lines(self, type_):
         '''
-        Return move line values for stock move
+        Return move lines for stock move
         '''
-        uom_obj = Pool().get('product.uom')
-        currency_obj = Pool().get('currency.currency')
+        pool = Pool()
+        Uom = pool.get('product.uom')
+        AccountMoveLine = pool.get('account.move.line')
         assert type_.startswith('in_') or type_.startswith('out_'), \
             'wrong type'
 
-        move_line = {
-            'name': move.rec_name,
-        }
+        with Transaction().set_user(0, set_context=True):
+            move_line = AccountMoveLine(name=self.rec_name)
         if (type_.endswith('supplier')
-                and move.product.cost_price_method != 'fixed'):
-            unit_price = move.unit_price
+                and self.product.cost_price_method != 'fixed'):
+            unit_price = self.unit_price
         else:
-            unit_price = uom_obj.compute_price(move.product.default_uom,
-                    move.cost_price, move.uom)
-        amount = currency_obj.round(move.company.currency,
-                Decimal(str(move.quantity)) * unit_price)
+            unit_price = Uom.compute_price(self.product.default_uom,
+                self.cost_price, self.uom)
+        amount = self.company.currency.round(
+                Decimal(str(self.quantity)) * unit_price)
 
         if type_.startswith('in_'):
-            move_line['debit'] = Decimal('0.0')
-            move_line['credit'] = amount
+            move_line.debit = Decimal('0.0')
+            move_line.credit = amount
             account_type = type_[3:]
         else:
-            move_line['debit'] = amount
-            move_line['credit'] = Decimal('0.0')
+            move_line.debit = amount
+            move_line.credit = Decimal('0.0')
             account_type = type_[4:]
 
-        move_line['account'] = getattr(move.product,
-                'account_stock_%s_used' % account_type).id
+        move_line.account = getattr(self.product,
+            'account_stock_%s_used' % account_type)
 
         return [move_line]
 
-    def _get_account_stock_move_line(self, move, amount):
+    def _get_account_stock_move_line(self, amount):
         '''
         Return counterpart move line value for stock move
         '''
-        move_line = {
-            'name': move.rec_name,
-            'account': move.product.account_stock_used.id,
-        }
+        pool = Pool()
+        AccountMoveLine = pool.get('account.move.line')
+        with Transaction().set_user(0, set_context=True):
+            move_line = AccountMoveLine(
+                name=self.rec_name,
+                account=self.product.account_stock_used,
+                )
         if not amount:
             return
         if amount >= Decimal('0.0'):
-            move_line['debit'] = Decimal('0.0')
-            move_line['credit'] = amount
+            move_line.debit = Decimal('0.0')
+            move_line.credit = amount
         else:
-            move_line['debit'] = - amount
-            move_line['credit'] = Decimal('0.0')
+            move_line.debit = - amount
+            move_line.credit = Decimal('0.0')
         return move_line
 
-    def _get_account_stock_move(self, move, move_lines):
+    def _get_account_stock_move(self, move_lines):
         '''
         Return account move value for stock move
         '''
-        date_obj = Pool().get('ir.date')
-        period_obj = Pool().get('account.period')
-        account_configuration_obj = Pool().get('account.configuration')
+        pool = Pool()
+        Date = pool.get('ir.date')
+        Period = pool.get('account.period')
+        AccountConfiguration = pool.get('account.configuration')
+        AccountMove = pool.get('account.move')
 
-        date = move.effective_date or date_obj.today()
-        period_id = period_obj.find(move.company.id, date=date)
-        account_configuration = account_configuration_obj.browse(1)
-        journal_id = account_configuration.stock_journal.id
-        return {
-            'journal': journal_id,
-            'period': period_id,
-            'date': date,
-            'lines': [('create', line) for line in move_lines],
-        }
+        date = self.effective_date or Date.today()
+        period_id = Period.find(self.company.id, date=date)
+        account_configuration = AccountConfiguration(1)
+        with Transaction().set_user(0, set_context=True):
+            return AccountMove(
+                journal=account_configuration.stock_journal,
+                period=period_id,
+                date=date,
+                lines=move_lines,
+                )
 
-    def _get_account_stock_move_type(self, move):
+    def _get_account_stock_move_type(self):
         '''
         Get account move type
         '''
-        type_ = (move.from_location.type, move.to_location.type)
+        type_ = (self.from_location.type, self.to_location.type)
         if type_ == ('supplier', 'storage'):
             return 'in_supplier'
         elif type_ == ('storage', 'supplier'):
@@ -106,70 +114,64 @@
         elif type_ == ('customer', 'supplier'):
             return 'customer_supplier'
 
-    def _create_account_stock_move(self, move):
+    def _create_account_stock_move(self):
         '''
         Create account move for stock move
         '''
-        account_move_obj = Pool().get('account.move')
-        if move.product.type != 'goods':
+        AccountMove = Pool().get('account.move')
+        if self.product.type != 'goods':
             return
-        type_ = self._get_account_stock_move_type(move)
+        type_ = self._get_account_stock_move_type()
         if not type_:
             return
-        assert not move.account_move, 'account move field not empty'
+        assert not self.account_move, 'account move field not empty'
         if type_ == 'supplier_customer':
-            account_move_lines = self._get_account_stock_move_lines(move,
+            account_move_lines = self._get_account_stock_move_lines(
                 'in_supplier')
-            account_move_lines.extend(self._get_account_stock_move_lines(move,
+            account_move_lines.extend(self._get_account_stock_move_lines(
                     'out_customer'))
         elif type_ == 'customer_supplier':
-            account_move_lines = self._get_account_stock_move_lines(move,
+            account_move_lines = self._get_account_stock_move_lines(
                 'in_customer')
-            account_move_lines.extend(self._get_account_stock_move_lines(move,
+            account_move_lines.extend(self._get_account_stock_move_lines(
                     'out_supplier'))
         else:
-            account_move_lines = self._get_account_stock_move_lines(move,
-                type_)
+            account_move_lines = self._get_account_stock_move_lines(type_)
 
         amount = Decimal('0.0')
         for line in account_move_lines:
-            amount += line['debit'] - line['credit']
-        move_line = self._get_account_stock_move_line(move, amount)
+            amount += line.debit - line.credit
+        move_line = self._get_account_stock_move_line(amount)
         if move_line:
             account_move_lines.append(move_line)
 
+        account_move = self._get_account_stock_move(account_move_lines)
+        account_move.save()
         with Transaction().set_user(0, set_context=True):
-            account_move_id = account_move_obj.create(
-                    self._get_account_stock_move(move, account_move_lines))
-            account_move_obj.post(account_move_id)
-        self.write(move.id, {
-            'account_move': account_move_id,
-            })
-        return account_move_id
+            AccountMove.post([account_move])
+        self.write([self], {
+                'account_move': account_move.id,
+                })
+        return account_move
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, moves, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default.setdefault('account_move', None)
-        return super(Move, self).copy(ids, default=default)
+        return super(Move, cls).copy(moves, default=default)
 
-    def create(self, vals):
-        new_id = super(Move, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        move = super(Move, cls).create(vals)
         if vals.get('state') == 'done':
-            move = self.browse(new_id)
-            self._create_account_stock_move(move)
-        return new_id
+            move._create_account_stock_move()
+        return move
 
-    def write(self, ids, vals):
-        res = super(Move, self).write(ids, vals)
+    @classmethod
+    def write(cls, moves, vals):
+        super(Move, cls).write(moves, vals)
         if vals.get('state') == 'done':
-            if isinstance(ids, (int, long)):
-                move = self.browse(ids)
-                self._create_account_stock_move(move)
-            else:
-                for move in self.browse(ids):
-                    self._create_account_stock_move(move)
-        return res
-
-Move()
+            for move in moves:
+                move._create_account_stock_move()
diff -r 3ae2e9579dcd trytond/modules/account_stock_continental/tests/scenario_account_stock_continental.rst
--- a/trytond/modules/account_stock_continental/tests/scenario_account_stock_continental.rst	Mon Sep 24 12:12:46 2012 +0200
+++ b/trytond/modules/account_stock_continental/tests/scenario_account_stock_continental.rst	Mon Sep 24 20:36:39 2012 +0200
@@ -154,7 +154,7 @@
     >>> product.account_stock_customer = stock_customer
     >>> product.account_stock_lost_found = stock_lost_found
     >>> product.save()
-    >>> product_average = Product(Product.copy(product.id, config.context))
+    >>> product_average = Product(Product.copy([product.id], config.context)[0])
     >>> product_average.cost_price_method = 'average'
     >>> product_average.save()
 
@@ -312,7 +312,7 @@
     >>> inventory = Inventory()
     >>> inventory.location = storage
     >>> inventory.save()
-    >>> Inventory.complete_lines(inventory.id, config.context)
+    >>> Inventory.complete_lines([inventory.id], config.context)
     >>> inventory_line = inventory.lines[0]
     >>> inventory_line.quantity = 1.0
     >>> inventory_line = inventory.lines[1]
diff -r 3b5e7803cf6c trytond/modules/analytic_account/__init__.py
--- a/trytond/modules/analytic_account/__init__.py	Fri Jun 08 19:33:42 2012 +0200
+++ b/trytond/modules/analytic_account/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,5 +1,21 @@
 #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.pool import Pool
 from .account import *
 from .line import *
+
+
+def register():
+    Pool.register(
+        Account,
+        OpenChartAccountStart,
+        AccountSelection,
+        AccountAccountSelection,
+        Line,
+        MoveLine,
+        module='analytic_account', type_='model')
+    Pool.register(
+        OpenChartAccount,
+        OpenAccount,
+        module='analytic_account', type_='wizard')
diff -r 3b5e7803cf6c trytond/modules/analytic_account/account.py
--- a/trytond/modules/analytic_account/account.py	Fri Jun 08 19:33:42 2012 +0200
+++ b/trytond/modules/analytic_account/account.py	Mon Sep 24 20:36:39 2012 +0200
@@ -8,19 +8,20 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Account', 'OpenChartAccountStart', 'OpenChartAccount',
+    'AccountSelection', 'AccountAccountSelection']
+
 
 class Account(ModelSQL, ModelView):
     'Analytic Account'
-    _name = 'analytic_account.account'
-    _description = __doc__
-
+    __name__ = 'analytic_account.account'
     name = fields.Char('Name', required=True, translate=True, select=True)
     code = fields.Char('Code', select=True)
     active = fields.Boolean('Active', select=True)
     company = fields.Many2One('company.company', 'Company')
     currency = fields.Many2One('currency.currency', 'Currency', required=True)
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['currency']), 'get_currency_digits')
+        on_change_with=['currency']), 'on_change_with_currency_digits')
     type = fields.Selection([
         ('root', 'Root'),
         ('view', 'View'),
@@ -65,68 +66,70 @@
             },
         depends=['type'])
 
-    def __init__(self):
-        super(Account, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Account, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_accounts'),
-        ]
-        self._error_messages.update({
-            'recursive_accounts': 'You can not create recursive accounts!',
-        })
-        self._order.insert(0, ('code', 'ASC'))
+            ]
+        cls._error_messages.update({
+                'recursive_accounts': 'You can not create recursive accounts!',
+                })
+        cls._order.insert(0, ('code', 'ASC'))
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_currency(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency():
+        Company = Pool().get('company.company')
         if Transaction().context.get('company'):
-            company = company_obj.browse(Transaction().context['company'])
+            company = Company(Transaction().context['company'])
             return company.currency.id
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'normal'
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_display_balance(self):
+    @staticmethod
+    def default_display_balance():
         return 'credit-debit'
 
-    def default_mandatory(self):
+    @staticmethod
+    def default_mandatory():
         return False
 
-    def on_change_with_currency_digits(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-            return currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.currency:
+            return self.currency.digits
         return 2
 
-    def get_currency_digits(self, ids, name):
+    @classmethod
+    def get_balance(cls, accounts, name):
         res = {}
-        for account in self.browse(ids):
-            res[account.id] = account.currency.digits
-        return res
-
-    def get_balance(self, ids, name):
-        res = {}
-        line_obj = Pool().get('analytic_account.line')
-        currency_obj = Pool().get('currency.currency')
+        Line = Pool().get('analytic_account.line')
+        Currency = Pool().get('currency.currency')
         cursor = Transaction().cursor
 
-        child_ids = self.search([('parent', 'child_of', ids)])
-        all_ids = {}.fromkeys(ids + child_ids).keys()
+        ids = [a.id for a in accounts]
+        childs = cls.search([('parent', 'child_of', ids)])
+        all_ids = {}.fromkeys(ids + [c.id for c in childs]).keys()
 
         id2account = {}
-        accounts = self.browse(all_ids)
-        for account in accounts:
+        all_accounts = cls.browse(all_ids)
+        for account in all_accounts:
             id2account[account.id] = account
 
-        line_query = line_obj.query_get()
+        line_query = Line.query_get()
         cursor.execute('SELECT a.id, ' \
                     'SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))), ' \
                     'c.currency ' \
@@ -154,46 +157,48 @@
                 if currency_id in id2currency:
                     currency = id2currency[currency_id]
                 else:
-                    currency = currency_obj.browse(currency_id)
+                    currency = Currency(currency_id)
                     id2currency[currency.id] = currency
-                account_sum[account_id] += currency_obj.compute(currency, sum,
+                account_sum[account_id] += Currency.compute(currency, sum,
                         id2account[account_id].currency, round=True)
             else:
-                account_sum[account_id] += currency_obj.round(
-                        id2account[account_id].currency, sum)
+                account_sum[account_id] += \
+                    id2account[account_id].currency.round(sum)
 
         for account_id in ids:
             res.setdefault(account_id, Decimal('0.0'))
-            child_ids = self.search([
-                ('parent', 'child_of', [account_id]),
-                ])
+            childs = cls.search([
+                    ('parent', 'child_of', [account_id]),
+                    ])
             to_currency = id2account[account_id].currency
-            for child_id in child_ids:
-                from_currency = id2account[child_id].currency
-                res[account_id] += currency_obj.compute(from_currency,
-                        account_sum.get(child_id, Decimal('0.0')), to_currency,
+            for child in childs:
+                from_currency = id2account[child.id].currency
+                res[account_id] += Currency.compute(from_currency,
+                        account_sum.get(child.id, Decimal('0.0')), to_currency,
                         round=True)
-            res[account_id] = currency_obj.round(to_currency, res[account_id])
+            res[account_id] = to_currency.round(res[account_id])
             if id2account[account_id].display_balance == 'credit-debit':
                 res[account_id] = - res[account_id]
         return res
 
-    def get_credit_debit(self, ids, name):
+    @classmethod
+    def get_credit_debit(cls, accounts, name):
         res = {}
-        line_obj = Pool().get('analytic_account.line')
-        currency_obj = Pool().get('currency.currency')
+        pool = Pool()
+        Line = pool.get('analytic_account.line')
+        Currency = pool.get('currency.currency')
         cursor = Transaction().cursor
 
         if name not in ('credit', 'debit'):
             raise Exception('Bad argument')
 
         id2account = {}
-        accounts = self.browse(ids)
+        ids = [a.id for a in accounts]
         for account in accounts:
             res[account.id] = Decimal('0.0')
             id2account[account.id] = account
 
-        line_query = line_obj.query_get()
+        line_query = Line.query_get()
         cursor.execute('SELECT a.id, ' \
                     'SUM(COALESCE(l.' + name + ', 0)), ' \
                     'c.currency ' \
@@ -220,94 +225,85 @@
                 if currency_id in id2currency:
                     currency = id2currency[currency_id]
                 else:
-                    currency = currency_obj.browse(currency_id)
+                    currency = Currency(currency_id)
                     id2currency[currency.id] = currency
-                res[account_id] += currency_obj.compute(currency, sum,
+                res[account_id] += Currency.compute(currency, sum,
                         id2account[account_id].currency, round=True)
             else:
-                res[account_id] += currency_obj.round(
-                        id2account[account_id].currency, sum)
+                res[account_id] += id2account[account_id].currency.round(sum)
         return res
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for account in self.browse(ids):
-            if account.code:
-                res[account.id] = account.code + ' - ' + unicode(account.name)
-            else:
-                res[account.id] = unicode(account.name)
-        return res
+    def get_rec_name(self, name):
+        if self.code:
+            return self.code + ' - ' + unicode(self.name)
+        else:
+            return unicode(self.name)
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([('code',) + clause[1:]], limit=1)
-        if ids:
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        accounts = cls.search([('code',) + clause[1:]], limit=1)
+        if accounts:
             return [('code',) + clause[1:]]
         else:
-            return [(self._rec_name,) + clause[1:]]
+            return [(cls._rec_name,) + clause[1:]]
 
-    def convert_view(self, tree):
+    @classmethod
+    def convert_view(cls, tree):
         res = tree.xpath('//field[@name=\'analytic_accounts\']')
         if not res:
             return
         element_accounts = res[0]
 
-        root_account_ids = self.search([
-            ('parent', '=', None),
-            ])
-        if not root_account_ids:
+        root_accounts = cls.search([
+                ('parent', '=', None),
+                ])
+        if not root_accounts:
             element_accounts.getparent().getparent().remove(
                     element_accounts.getparent())
             return
-        for account_id in root_account_ids:
+        for account in root_accounts:
             newelement = copy.copy(element_accounts)
             newelement.tag = 'label'
-            newelement.set('name', 'analytic_account_' + str(account_id))
+            newelement.set('name', 'analytic_account_' + str(account.id))
             element_accounts.addprevious(newelement)
             newelement = copy.copy(element_accounts)
-            newelement.set('name', 'analytic_account_' + str(account_id))
+            newelement.set('name', 'analytic_account_' + str(account.id))
             element_accounts.addprevious(newelement)
         parent = element_accounts.getparent()
         parent.remove(element_accounts)
 
-    def analytic_accounts_fields_get(self, field, fields_names=None):
+    @classmethod
+    def analytic_accounts_fields_get(cls, field, fields_names=None):
         res = {}
         if fields_names is None:
             fields_names = []
 
-        root_account_ids = self.search([
-            ('parent', '=', None),
-            ])
-        for account in self.browse(root_account_ids):
+        root_accounts = cls.search([
+                ('parent', '=', None),
+                ])
+        for account in root_accounts:
             name = 'analytic_account_' + str(account.id)
             if name in fields_names or not fields_names:
                 res[name] = field.copy()
                 res[name]['required'] = account.mandatory
                 res[name]['string'] = account.name
-                res[name]['relation'] = self._name
+                res[name]['relation'] = cls.__name__
                 res[name]['domain'] = PYSONEncoder().encode([
                     ('root', '=', account.id),
                     ('type', '=', 'normal')])
         return res
 
-Account()
-
 
 class OpenChartAccountStart(ModelView):
     'Open Chart of Accounts'
-    _name = 'analytic_account.open_chart.start'
-    _description = __doc__
+    __name__ = 'analytic_account.open_chart.start'
     start_date = fields.Date('Start Date')
     end_date = fields.Date('End Date')
 
-OpenChartAccountStart()
-
 
 class OpenChartAccount(Wizard):
     'Open Chart of Accounts'
-    _name = 'analytic_account.open_chart'
-
+    __name__ = 'analytic_account.open_chart'
     start = StateView('analytic_account.open_chart.start',
         'analytic_account.open_chart_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -315,49 +311,46 @@
             ])
     open_ = StateAction('analytic_account.act_account_tree2')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         action['pyson_context'] = PYSONEncoder().encode({
-                'start_date': session.start.start_date,
-                'end_date': session.start.end_date,
+                'start_date': self.start.start_date,
+                'end_date': self.start.end_date,
                 })
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
 
-OpenChartAccount()
-
 
 class AccountSelection(ModelSQL, ModelView):
     'Analytic Account Selection'
-    _name = 'analytic_account.account.selection'
-    _description = __doc__
+    __name__ = 'analytic_account.account.selection'
     _rec_name = 'id'
 
     accounts = fields.Many2Many(
             'analytic_account.account-analytic_account.account.selection',
             'selection', 'account', 'Accounts')
 
-    def __init__(self):
-        super(AccountSelection, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(AccountSelection, cls).__setup__()
+        cls._constraints += [
             ('check_root', 'root_account'),
-        ]
-        self._error_messages.update({
-            'root_account': 'Can not have many accounts with the same root ' \
-                    'or a missing mandatory root account!',
-        })
+            ]
+        cls._error_messages.update({
+                'root_account': 'Can not have many accounts with the same ' \
+                    'root or a missing mandatory root account!',
+                })
 
-    def check_root(self, ids):
+    @classmethod
+    def check_root(cls, selections):
         "Check Root"
-        account_obj = Pool().get('analytic_account.account')
+        Account = Pool().get('analytic_account.account')
 
-        root_account_ids = account_obj.search([
+        root_accounts = Account.search([
             ('parent', '=', None),
             ])
-        root_accounts = account_obj.browse(root_account_ids)
 
-        selections = self.browse(ids)
         for selection in selections:
             roots = []
             for account in selection.accounts:
@@ -371,17 +364,12 @@
                             return False
         return True
 
-AccountSelection()
-
 
 class AccountAccountSelection(ModelSQL):
     'Analytic Account - Analytic Account Selection'
-    _name = 'analytic_account.account-analytic_account.account.selection'
-    _description = __doc__
+    __name__ = 'analytic_account.account-analytic_account.account.selection'
     _table = 'analytic_account_account_selection_rel'
     selection = fields.Many2One('analytic_account.account.selection',
             'Selection', ondelete='CASCADE', required=True, select=True)
     account = fields.Many2One('analytic_account.account', 'Account',
             ondelete='RESTRICT', required=True, select=True)
-
-AccountAccountSelection()
diff -r 3b5e7803cf6c trytond/modules/analytic_account/line.py
--- a/trytond/modules/analytic_account/line.py	Fri Jun 08 19:33:42 2012 +0200
+++ b/trytond/modules/analytic_account/line.py	Mon Sep 24 20:36:39 2012 +0200
@@ -9,21 +9,21 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Line', 'MoveLine', 'OpenAccount']
+
 
 class Line(ModelSQL, ModelView):
     'Analytic Line'
-    _name = 'analytic_account.line'
-    _description = __doc__
-
+    __name__ = 'analytic_account.line'
     name = fields.Char('Name', required=True)
     debit = fields.Numeric('Debit', digits=(16, Eval('currency_digits', 2)),
         required=True, depends=['currency_digits'])
     credit = fields.Numeric('Credit', digits=(16, Eval('currency_digits', 2)),
         required=True, depends=['currency_digits'])
     currency = fields.Function(fields.Many2One('currency.currency', 'Currency',
-        on_change_with=['move_line']), 'get_currency')
+        on_change_with=['move_line']), 'on_change_with_currency')
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['move_line']), 'get_currency_digits')
+        on_change_with=['move_line']), 'on_change_with_currency_digits')
     account = fields.Many2One('analytic_account.account', 'Account',
             required=True, select=True, domain=[('type', '!=', 'view')])
     move_line = fields.Many2One('account.move.line', 'Account Move Line',
@@ -35,69 +35,60 @@
     party = fields.Many2One('party.party', 'Party')
     active = fields.Boolean('Active', select=True)
 
-    def __init__(self):
-        super(Line, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Line, cls).__setup__()
+        cls._sql_constraints += [
             ('credit_debit',
                 'CHECK((credit * debit = 0.0) AND (credit + debit >= 0.0))',
                 'Wrong credit/debit values!'),
-        ]
-        self._constraints += [
+            ]
+        cls._constraints += [
             ('check_account', 'line_on_view_inactive_account'),
-        ]
-        self._error_messages.update({
+            ]
+        cls._error_messages.update({
             'line_on_view_inactive_account': 'You can not create move line\n' \
                     'on view/inactive account!',
-        })
-        self._order.insert(0, ('date', 'ASC'))
+            })
+        cls._order.insert(0, ('date', 'ASC'))
 
-    def init(self, module_name):
-        super(Line, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Line, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.2 currency has been changed in function field
         table.not_null_action('currency', action='remove')
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_debit(self):
+    @staticmethod
+    def default_debit():
         return Decimal(0)
 
-    def default_credit(self):
+    @staticmethod
+    def default_credit():
         return Decimal(0)
 
-    def on_change_with_currency(self, vals):
-        move_line_obj = Pool().get('account.move.line')
-        if vals.get('move_line'):
-            move_line = move_line_obj.browse(vals['move_line'])
-            return move_line.account.company.currency.id
+    def on_change_with_currency(self, name=None):
+        if self.move_line:
+            return self.move_line.account.company.currency.id
 
-    def get_currency(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            res[line.id] = line.move_line.account.company.currency.id
-        return res
-
-    def on_change_with_currency_digits(self, vals):
-        move_line_obj = Pool().get('account.move.line')
-        if vals.get('move_line'):
-            move_line = move_line_obj.browse(vals['move_line'])
-            return move_line.account.company.currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.move_line:
+            return self.move_line.account.company.currency.digits
         return 2
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            res[line.id] = line.move_line.account.company.currency.digits
-        return res
-
-    def query_get(self, obj='l'):
+    @staticmethod
+    def query_get(obj='l'):
         '''
         Return SQL clause for analytic line depending of the context.
         obj is the SQL alias of the analytic_account_line in the query.
@@ -115,32 +106,27 @@
                     str(Transaction().context['end_date']) + '\')'
         return res
 
-    def check_account(self, ids):
-        for line in self.browse(ids):
-            if line.account.type == 'view':
-                return False
-            if not line.account.active:
-                return False
+    def check_account(self):
+        if self.account.type == 'view':
+            return False
+        if not self.account.active:
+            return False
         return True
 
-Line()
-
 
 class MoveLine(ModelSQL, ModelView):
-    _name = 'account.move.line'
+    __name__ = 'account.move.line'
     analytic_lines = fields.One2Many('analytic_account.line', 'move_line',
             'Analytic Lines')
 
-MoveLine()
-
 
 class OpenAccount(Wizard):
     'Open Account'
-    _name = 'analytic_account.line.open_account'
+    __name__ = 'analytic_account.line.open_account'
     start_state = 'open_'
     open_ = StateAction('analytic_account.act_line_form')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         action['pyson_domain'] = [
             ('account', '=', Transaction().context['active_id']),
             ]
@@ -155,7 +141,5 @@
         action['pyson_domain'] = PYSONEncoder().encode(action['pyson_domain'])
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
-
-OpenAccount()
diff -r 9f175bd4bd58 trytond/modules/analytic_invoice/__init__.py
--- a/trytond/modules/analytic_invoice/__init__.py	Fri Jun 08 19:33:43 2012 +0200
+++ b/trytond/modules/analytic_invoice/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,4 +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.pool import Pool
 from .invoice import *
+
+
+def register():
+    Pool.register(
+        InvoiceLine,
+        Account,
+        module='analytic_invoice', type_='model')
diff -r 9f175bd4bd58 trytond/modules/analytic_invoice/invoice.py
--- a/trytond/modules/analytic_invoice/invoice.py	Fri Jun 08 19:33:43 2012 +0200
+++ b/trytond/modules/analytic_invoice/invoice.py	Mon Sep 24 20:36:39 2012 +0200
@@ -3,12 +3,14 @@
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.pyson import Eval
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['InvoiceLine', 'Account']
+__metaclass__ = PoolMeta
 
-class InvoiceLine(ModelSQL, ModelView):
-    _name = 'account.invoice.line'
 
+class InvoiceLine:
+    __name__ = 'account.invoice.line'
     analytic_accounts = fields.Many2One('analytic_account.account.selection',
         'Analytic Accounts',
         states={
@@ -16,47 +18,45 @@
             },
         depends=['type'])
 
-    def _view_look_dom_arch(self, tree, type, field_children=None):
-        analytic_account_obj = Pool().get('analytic_account.account')
-        analytic_account_obj.convert_view(tree)
-        arch, fields = super(InvoiceLine, self)._view_look_dom_arch(tree, type,
+    @classmethod
+    def _view_look_dom_arch(cls, tree, type, field_children=None):
+        AnalyticAccount = Pool().get('analytic_account.account')
+        AnalyticAccount.convert_view(tree)
+        return super(InvoiceLine, cls)._view_look_dom_arch(tree, type,
             field_children=field_children)
-        return arch, fields
 
-    def fields_get(self, fields_names=None):
-        analytic_account_obj = Pool().get('analytic_account.account')
+    @classmethod
+    def fields_get(cls, fields_names=None):
+        AnalyticAccount = Pool().get('analytic_account.account')
 
-        res = super(InvoiceLine, self).fields_get(fields_names)
+        fields = super(InvoiceLine, cls).fields_get(fields_names)
 
-        analytic_accounts_field = super(InvoiceLine, self).fields_get(
+        analytic_accounts_field = super(InvoiceLine, cls).fields_get(
                 ['analytic_accounts'])['analytic_accounts']
 
-        res.update(analytic_account_obj.analytic_accounts_fields_get(
-            analytic_accounts_field, fields_names))
-        return res
+        fields.update(AnalyticAccount.analytic_accounts_fields_get(
+                analytic_accounts_field, fields_names))
+        return fields
 
-    def default_get(self, fields, with_rec_name=True):
+    @classmethod
+    def default_get(cls, fields, with_rec_name=True):
         fields = [x for x in fields if not x.startswith('analytic_account_')]
-        return super(InvoiceLine, self).default_get(fields,
+        return super(InvoiceLine, cls).default_get(fields,
             with_rec_name=with_rec_name)
 
-    def read(self, ids, fields_names=None):
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
+    @classmethod
+    def read(cls, ids, fields_names=None):
         if fields_names:
             fields_names2 = [x for x in fields_names
                     if not x.startswith('analytic_account_')]
         else:
             fields_names2 = fields_names
 
-        res = super(InvoiceLine, self).read(ids, fields_names=fields_names2)
+        res = super(InvoiceLine, cls).read(ids, fields_names=fields_names2)
 
         if not fields_names:
-            fields_names = list(set(self._columns.keys() \
-                    + self._inherit_fields.keys()))
+            fields_names = list(set(cls._fields.keys() \
+                    + cls._inherit_fields.keys()))
 
         root_ids = []
         for field in fields_names:
@@ -66,7 +66,7 @@
             id2record = {}
             for record in res:
                 id2record[record['id']] = record
-            lines = self.browse(ids)
+            lines = cls.browse(ids)
             for line in lines:
                 for root_id in root_ids:
                     id2record[line.id]['analytic_account_' \
@@ -84,12 +84,11 @@
                                     str(account.root.id) + '.'):
                                 ham, field2 = field.split('.', 1)
                                 id2record[line.id][field] = account[field2]
-        if int_id:
-            return res[0]
         return res
 
-    def create(self, vals):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def create(cls, vals):
+        Selection = Pool().get('analytic_account.account.selection')
         vals = vals.copy()
         selection_vals = {}
         for field in vals.keys():
@@ -99,17 +98,17 @@
                     selection_vals['accounts'].append(('add', vals[field]))
                 del vals[field]
         if vals.get('analytic_accounts'):
-            selection_obj.write(vals['analytic_accounts'], selection_vals)
+            Selection.write([Selection(vals['analytic_accounts'])],
+                selection_vals)
         elif vals.get('type', 'line') == 'line':
-            selection_id = selection_obj.create(selection_vals)
-            vals['analytic_accounts'] = selection_id
-        return super(InvoiceLine, self).create(vals)
+            selection = Selection.create(selection_vals)
+            vals['analytic_accounts'] = selection.id
+        return super(InvoiceLine, cls).create(vals)
 
-    def write(self, ids, vals):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def write(cls, lines, vals):
+        Selection = Pool().get('analytic_account.account.selection')
         vals = vals.copy()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
         selection_vals = {}
         for field in vals.keys():
             if field.startswith('analytic_account_'):
@@ -117,7 +116,6 @@
                 selection_vals[root_id] = vals[field]
                 del vals[field]
         if selection_vals:
-            lines = self.browse(ids)
             for line in lines:
                 if line.type != 'line':
                     continue
@@ -125,11 +123,10 @@
                 if not line.analytic_accounts:
                     # Create missing selection
                     with Transaction().set_user(0):
-                            selection_id = selection_obj.create({})
-                    self.write(line.id, {
-                        'analytic_accounts': selection_id,
-                        })
-                    line = self.browse(line.id)
+                            selection = Selection.create({})
+                    cls.write([line], {
+                            'analytic_accounts': selection.id,
+                            })
                 for account in line.analytic_accounts.accounts:
                     if account.root.id in selection_vals:
                         value = selection_vals[account.root.id]
@@ -141,103 +138,90 @@
                     if account_id \
                             and account_id not in accounts:
                         accounts.append(account_id)
-                selection_obj.write(line.analytic_accounts.id, {
-                    'accounts': [('set', accounts)],
-                    })
-        return super(InvoiceLine, self).write(ids, vals)
+                Selection.write([line.analytic_accounts], {
+                        'accounts': [('set', accounts)],
+                        })
+        super(InvoiceLine, cls).write(lines, vals)
 
-    def delete(self, ids):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def delete(cls, lines):
+        Selection = Pool().get('analytic_account.account.selection')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
         selection_ids = []
-        lines = self.browse(ids)
         for line in lines:
             if line.analytic_accounts:
                 selection_ids.append(line.analytic_accounts.id)
 
-        res = super(InvoiceLine, self).delete(ids)
-        selection_obj.delete(selection_ids)
-        return res
+        super(InvoiceLine, cls).delete(lines)
+        Selection.delete(Selection.browse(selection_ids))
 
-    def copy(self, ids, default=None):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def copy(cls, lines, default=None):
+        Selection = Pool().get('analytic_account.account.selection')
 
-        new_ids = super(InvoiceLine, self).copy(ids, default=default)
+        new_lines = super(InvoiceLine, cls).copy(lines, default=default)
 
-        int_id = False
-        if isinstance(new_ids, (int, long)):
-            int_id = True
-            new_ids = [new_ids]
+        for line in new_lines:
+            if line.analytic_accounts:
+                selection, = Selection.copy([line.analytic_accounts])
+                cls.write([line], {
+                        'analytic_accounts': selection.id,
+                        })
+        return new_lines
 
-        for line in self.browse(new_ids):
-            if line.analytic_accounts:
-                selection_id = selection_obj.copy(line.analytic_accounts.id)
-                self.write(line.id, {
-                    'analytic_accounts': selection_id,
-                    })
+    def _credit(self):
+        Selection = Pool().get('analytic_account.account.selection')
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
+        result = super(InvoiceLine, self)._credit()
 
-    def _credit(self, line):
-        selection_obj = Pool().get('analytic_account.account.selection')
-
-        result = super(InvoiceLine, self)._credit(line)
-
-        if line.analytic_accounts:
-            selection_id = selection_obj.copy(line.analytic_accounts.id)
-            result['analytic_accounts'] = selection_id
+        if self.analytic_accounts:
+            selection, = Selection.copy([self.analytic_accounts])
+            result['analytic_accounts'] = selection.id
         return result
 
-    def get_move_line(self, line):
-        values = super(InvoiceLine, self).get_move_line(line)
-        if line.analytic_accounts and line.analytic_accounts.accounts:
+    def get_move_line(self):
+        values = super(InvoiceLine, self).get_move_line()
+        if self.analytic_accounts and self.analytic_accounts.accounts:
             for value in values:
                 value['analytic_lines'] = []
-                for account in line.analytic_accounts.accounts:
+                for account in self.analytic_accounts.accounts:
                     vals = {}
-                    vals['name'] = line.description
+                    vals['name'] = self.description
                     vals['debit'] = value['debit']
                     vals['credit'] = value['credit']
                     vals['account'] = account.id
-                    vals['journal'] = line.invoice.journal.id
-                    vals['date'] = line.invoice.invoice_date
-                    vals['reference'] = line.invoice.reference
-                    vals['party'] = line.invoice.party.id
+                    vals['journal'] = self.invoice.journal.id
+                    vals['date'] = self.invoice.invoice_date
+                    vals['reference'] = self.invoice.reference
+                    vals['party'] = self.invoice.party.id
                     value['analytic_lines'].append(('create', vals))
         return values
 
-InvoiceLine()
-
 
 class Account(ModelSQL, ModelView):
-    _name = 'analytic_account.account'
+    __name__ = 'analytic_account.account'
 
-    def delete(self, ids):
-        account_invoice_line_obj = Pool().get('account.invoice.line')
-        res = super(Account, self).delete(ids)
+    @classmethod
+    def delete(cls, accounts):
+        InvoiceLine = Pool().get('account.invoice.line')
+        super(Account, cls).delete(accounts)
         # Restart the cache on the fields_view_get method of
         # account.invoice.line
-        account_invoice_line_obj.fields_view_get.reset()
-        return res
+        InvoiceLine._fields_view_get_cache.clear()
 
-    def create(self, vals):
-        account_invoice_line_obj = Pool().get('account.invoice.line')
-        res = super(Account, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        InvoiceLine = Pool().get('account.invoice.line')
+        account = super(Account, cls).create(vals)
         # Restart the cache on the fields_view_get method of
         # account.invoice.line
-        account_invoice_line_obj.fields_view_get.reset()
-        return res
+        InvoiceLine._fields_view_get_cache.clear()
+        return account
 
-    def write(self, ids, vals):
-        account_invoice_line_obj = Pool().get('account.invoice.line')
-        res = super(Account, self).write(ids, vals)
+    @classmethod
+    def write(cls, accounts, vals):
+        InvoiceLine = Pool().get('account.invoice.line')
+        super(Account, cls).write(accounts, vals)
         # Restart the cache on the fields_view_get method of
         # account.invoice.line
-        account_invoice_line_obj.fields_view_get.reset()
-        return res
-
-Account()
+        InvoiceLine._fields_view_get_cache.clear()
diff -r 1498dab1479d trytond/modules/analytic_purchase/__init__.py
--- a/trytond/modules/analytic_purchase/__init__.py	Fri Jun 08 19:33:45 2012 +0200
+++ b/trytond/modules/analytic_purchase/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,4 +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.
 
+from trytond.pool import Pool
 from .purchase import *
+
+
+def register():
+    Pool.register(
+        Purchase,
+        PurchaseLine,
+        Account,
+        module='analytic_purchase', type_='model')
diff -r 1498dab1479d trytond/modules/analytic_purchase/purchase.py
--- a/trytond/modules/analytic_purchase/purchase.py	Fri Jun 08 19:33:45 2012 +0200
+++ b/trytond/modules/analytic_purchase/purchase.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,93 +1,89 @@
 #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 ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Purchase', 'PurchaseLine', 'Account']
+__metaclass__ = PoolMeta
 
-class Purchase(ModelSQL, ModelView):
-    _name = "purchase.purchase"
 
-    def __init__(self):
-        super(Purchase, self).__init__()
-        self._error_messages.update({
+class Purchase:
+    __name__ = "purchase.purchase"
+
+    @classmethod
+    def __setup__(cls):
+        super(Purchase, cls).__setup__()
+        cls._error_messages.update({
             'analytic_account_required': 'Analytic account is required ' \
                     'on line "%s" (%d)!',
             })
 
-    def check_for_quotation(self, ids):
-        account_selection_obj = Pool().get(
-                'analytic_account.account.selection')
+    def check_for_quotation(self):
+        AccountSelection = Pool().get('analytic_account.account.selection')
 
-        super(Purchase, self).check_for_quotation(ids)
+        super(Purchase, self).check_for_quotation()
 
-        purchases = self.browse(ids)
-        for purchase in purchases:
-            if not account_selection_obj.check_root(
-                    [x.analytic_accounts.id for x in purchase.lines
-                        if x.analytic_accounts.id]):
-                for line in purchase.lines:
-                    if line.type != 'line':
-                        continue
-                    if not account_selection_obj.check_root(
-                            [line.analytic_accounts.id]):
-                        self.raise_user_error('analytic_account_required',
-                                (line.rec_name, line.id))
+        if not AccountSelection.check_root(
+                [x.analytic_accounts for x in self.lines
+                    if x.analytic_accounts]):
+            for line in self.lines:
+                if line.type != 'line':
+                    continue
+                if not AccountSelection.check_root([line.analytic_accounts]):
+                    self.raise_user_error('analytic_account_required',
+                            (line.rec_name, line.id))
 
-Purchase()
 
-
-class PurchaseLine(ModelSQL, ModelView):
-    _name = 'purchase.line'
-
+class PurchaseLine:
+    __name__ = 'purchase.line'
     analytic_accounts = fields.Many2One('analytic_account.account.selection',
         'Analytic Accounts',
         states={
             'invisible': Eval('type') != 'line',
             }, depends=['type'])
 
-    def _view_look_dom_arch(self, tree, type, field_children=None):
-        analytic_account_obj = Pool().get('analytic_account.account')
-        analytic_account_obj.convert_view(tree)
-        arch, fields = super(PurchaseLine, self)._view_look_dom_arch(tree,
+    @classmethod
+    def _view_look_dom_arch(cls, tree, type, field_children=None):
+        AnalyticAccount = Pool().get('analytic_account.account')
+        AnalyticAccount.convert_view(tree)
+        arch, fields = super(PurchaseLine, cls)._view_look_dom_arch(tree,
             type, field_children=field_children)
         return arch, fields
 
-    def fields_get(self, fields_names=None):
-        analytic_account_obj = Pool().get('analytic_account.account')
+    @classmethod
+    def fields_get(cls, fields_names=None):
+        AnalyticAccount = Pool().get('analytic_account.account')
 
-        res = super(PurchaseLine, self).fields_get(fields_names)
+        res = super(PurchaseLine, cls).fields_get(fields_names)
 
-        analytic_accounts_field = super(PurchaseLine, self).fields_get(
+        analytic_accounts_field = super(PurchaseLine, cls).fields_get(
                 ['analytic_accounts'])['analytic_accounts']
 
-        res.update(analytic_account_obj.analytic_accounts_fields_get(
+        res.update(AnalyticAccount.analytic_accounts_fields_get(
             analytic_accounts_field, fields_names))
         return res
 
-    def default_get(self, fields, with_rec_name=True):
+    @classmethod
+    def default_get(cls, fields, with_rec_name=True):
         fields = [x for x in fields if not x.startswith('analytic_account_')]
-        return super(PurchaseLine, self).default_get(fields,
+        return super(PurchaseLine, cls).default_get(fields,
             with_rec_name=with_rec_name)
 
-    def read(self, ids, fields_names=None):
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
+    @classmethod
+    def read(cls, ids, fields_names=None):
         if fields_names:
             fields_names2 = [x for x in fields_names
                     if not x.startswith('analytic_account_')]
         else:
             fields_names2 = fields_names
 
-        res = super(PurchaseLine, self).read(ids, fields_names=fields_names2)
+        res = super(PurchaseLine, cls).read(ids, fields_names=fields_names2)
 
         if not fields_names:
-            fields_names = list(set(self._columns.keys() \
-                    + self._inherit_fields.keys()))
+            fields_names = list(set(cls._fields.keys()
+                    + cls._inherit_fields.keys()))
 
         root_ids = []
         for field in fields_names:
@@ -97,7 +93,7 @@
             id2record = {}
             for record in res:
                 id2record[record['id']] = record
-            lines = self.browse(ids)
+            lines = cls.browse(ids)
             for line in lines:
                 for root_id in root_ids:
                     id2record[line.id]['analytic_account_' \
@@ -115,12 +111,11 @@
                                     str(account.root.id) + '.'):
                                 ham, field2 = field.split('.', 1)
                                 id2record[line.id][field] = account[field2]
-        if int_id:
-            return res[0]
         return res
 
-    def create(self, vals):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def create(cls, vals):
+        Selection = Pool().get('analytic_account.account.selection')
         vals = vals.copy()
         selection_vals = {}
         for field in vals.keys():
@@ -130,17 +125,17 @@
                     selection_vals['accounts'].append(('add', vals[field]))
                 del vals[field]
         if vals.get('analytic_accounts'):
-            selection_obj.write(vals['analytic_accounts'], selection_vals)
+            Selection.write([Selection(vals['analytic_accounts'])],
+                selection_vals)
         elif vals.get('type', 'line') == 'line':
-            selection_id = selection_obj.create(selection_vals)
-            vals['analytic_accounts'] = selection_id
-        return super(PurchaseLine, self).create(vals)
+            selection = Selection.create(selection_vals)
+            vals['analytic_accounts'] = selection.id
+        return super(PurchaseLine, cls).create(vals)
 
-    def write(self, ids, vals):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def write(cls, lines, vals):
+        Selection = Pool().get('analytic_account.account.selection')
         vals = vals.copy()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
         selection_vals = {}
         for field in vals.keys():
             if field.startswith('analytic_account_'):
@@ -148,7 +143,6 @@
                 selection_vals[root_id] = vals[field]
                 del vals[field]
         if selection_vals:
-            lines = self.browse(ids)
             for line in lines:
                 if line.type != 'line':
                     continue
@@ -156,11 +150,10 @@
                 if not line.analytic_accounts:
                     # Create missing selection
                     with Transaction().set_user(0):
-                        selection_id = selection_obj.create({})
-                    self.write(line.id, {
-                        'analytic_accounts': selection_id,
+                        selection = Selection.create({})
+                    cls.write([line], {
+                        'analytic_accounts': selection.id,
                         })
-                    line = self.browse(line.id)
                 for account in line.analytic_accounts.accounts:
                     if account.root.id in selection_vals:
                         value = selection_vals[account.root.id]
@@ -172,88 +165,73 @@
                     if account_id \
                             and account_id not in accounts:
                         accounts.append(account_id)
-                selection_obj.write(line.analytic_accounts.id, {
+                Selection.write([line.analytic_accounts], {
                     'accounts': [('set', accounts)],
                     })
-        return super(PurchaseLine, self).write(ids, vals)
+        return super(PurchaseLine, cls).write(lines, vals)
 
-    def delete(self, ids):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def delete(cls, lines):
+        Selection = Pool().get('analytic_account.account.selection')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        selection_ids = []
-        lines = self.browse(ids)
+        selections = []
         for line in lines:
             if line.analytic_accounts:
-                selection_ids.append(line.analytic_accounts.id)
+                selections.append(line.analytic_accounts)
 
-        res = super(PurchaseLine, self).delete(ids)
-        selection_obj.delete(selection_ids)
-        return res
+        super(PurchaseLine, cls).delete(lines)
+        Selection.delete(selections)
 
-    def copy(self, ids, default=None):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def copy(cls, lines, default=None):
+        Selection = Pool().get('analytic_account.account.selection')
 
-        new_ids = super(PurchaseLine, self).copy(ids, default=default)
+        new_lines = super(PurchaseLine, cls).copy(lines, default=default)
 
-        int_id = False
-        if isinstance(new_ids, (int, long)):
-            int_id = True
-            new_ids = [new_ids]
+        for line in lines:
+            if line.analytic_accounts:
+                selection, = Selection.copy([line.analytic_accounts])
+                cls.write([line], {
+                    'analytic_accounts': selection.id,
+                    })
+        return new_lines
 
-        for line in self.browse(new_ids):
-            if line.analytic_accounts:
-                selection_id = selection_obj.copy(line.analytic_accounts.id)
-                self.write(line.id, {
-                    'analytic_accounts': selection_id,
-                    })
+    def get_invoice_line(self):
+        AccountSelection = Pool().get('analytic_account.account.selection')
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
+        invoice_lines = super(PurchaseLine, self).get_invoice_line()
+        if not invoice_lines:
+            return invoice_lines
 
-    def get_invoice_line(self, line):
-        pool = Pool()
-        account_selection_obj = pool.get('analytic_account.account.selection')
+        selection = None
+        if self.analytic_accounts:
+            selection, = AccountSelection.copy([self.analytic_accounts])
+        for invoice_line in invoice_lines:
+            invoice_line.analytic_accounts = selection
+        return invoice_lines
 
-        res = super(PurchaseLine, self).get_invoice_line(line)
-        if not res:
-            return res
 
-        selection_id = False
-        if line.analytic_accounts:
-            selection_id = account_selection_obj.copy(
-                line.analytic_accounts.id)
-        for vals in res:
-            vals['analytic_accounts'] = selection_id
-        return res
+class Account:
+    __name__ = 'analytic_account.account'
 
-PurchaseLine()
+    @classmethod
+    def delete(cls, accounts):
+        PurchaseLine = Pool().get('purchase.line')
+        super(Account, cls).delete(accounts)
+        # Restart the cache on the fields_view_get method of purchase.line
+        PurchaseLine._fields_view_get_cache.clear()
 
+    @classmethod
+    def create(cls, vals):
+        PurchaseLine = Pool().get('purchase.line')
+        account = super(Account, cls).create(vals)
+        # Restart the cache on the fields_view_get method of purchase.line
+        PurchaseLine._fields_view_get_cache.clear()
+        return account
 
-class Account(ModelSQL, ModelView):
-    _name = 'analytic_account.account'
-
-    def delete(self, ids):
-        purchase_line_obj = Pool().get('purchase.line')
-        res = super(Account, self).delete(ids)
+    @classmethod
+    def write(cls, accounts, vals):
+        PurchaseLine = Pool().get('purchase.line')
+        super(Account, cls).write(accounts, vals)
         # Restart the cache on the fields_view_get method of purchase.line
-        purchase_line_obj.fields_view_get.reset()
-        return res
-
-    def create(self, vals):
-        purchase_line_obj = Pool().get('purchase.line')
-        res = super(Account, self).create(vals)
-        # Restart the cache on the fields_view_get method of purchase.line
-        purchase_line_obj.fields_view_get.reset()
-        return res
-
-    def write(self, ids, vals):
-        purchase_line_obj = Pool().get('purchase.line')
-        res = super(Account, self).write(ids, vals)
-        # Restart the cache on the fields_view_get method of purchase.line
-        purchase_line_obj.fields_view_get.reset()
-        return res
-
-Account()
+        PurchaseLine._fields_view_get_cache.clear()
diff -r bc4fc4480e15 trytond/modules/analytic_sale/__init__.py
--- a/trytond/modules/analytic_sale/__init__.py	Fri Jun 08 19:33:46 2012 +0200
+++ b/trytond/modules/analytic_sale/__init__.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,4 +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.pool import Pool
 from .sale import *
+
+
+def register():
+    Pool.register(
+        SaleLine,
+        Account,
+        module='analytic_sale', type_='model')
diff -r bc4fc4480e15 trytond/modules/analytic_sale/sale.py
--- a/trytond/modules/analytic_sale/sale.py	Fri Jun 08 19:33:46 2012 +0200
+++ b/trytond/modules/analytic_sale/sale.py	Mon Sep 24 20:36:39 2012 +0200
@@ -1,14 +1,16 @@
 #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 ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['SaleLine', 'Account']
+__metaclass__ = PoolMeta
 
-class SaleLine(ModelSQL, ModelView):
-    _name = 'sale.line'
 
+class SaleLine:
+    __name__ = 'sale.line'
     analytic_accounts = fields.Many2One('analytic_account.account.selection',
         'Analytic Accounts',
         states={
@@ -16,47 +18,46 @@
             },
         depends=['type'])
 
-    def _view_look_dom_arch(self, tree, type, field_children=None):
-        analytic_account_obj = Pool().get('analytic_account.account')
-        analytic_account_obj.convert_view(tree)
-        arch, fields, = super(SaleLine, self)._view_look_dom_arch(tree, type,
+    @classmethod
+    def _view_look_dom_arch(cls, tree, type, field_children=None):
+        AnalyticAccount = Pool().get('analytic_account.account')
+        AnalyticAccount.convert_view(tree)
+        arch, fields, = super(SaleLine, cls)._view_look_dom_arch(tree, type,
             field_children=field_children)
         return arch, fields
 
-    def fields_get(self, fields_names=None):
-        analytic_account_obj = Pool().get('analytic_account.account')
+    @classmethod
+    def fields_get(cls, fields_names=None):
+        AnalyticAccount = Pool().get('analytic_account.account')
 
-        res = super(SaleLine, self).fields_get(fields_names)
+        res = super(SaleLine, cls).fields_get(fields_names)
 
-        analytic_accounts_field = super(SaleLine, self).fields_get(
+        analytic_accounts_field = super(SaleLine, cls).fields_get(
                 ['analytic_accounts'])['analytic_accounts']
 
-        res.update(analytic_account_obj.analytic_accounts_fields_get(
+        res.update(AnalyticAccount.analytic_accounts_fields_get(
                 analytic_accounts_field, fields_names))
         return res
 
-    def default_get(self, fields, with_rec_name=True):
+    @classmethod
+    def default_get(cls, fields, with_rec_name=True):
         fields = [x for x in fields if not x.startswith('analytic_account_')]
-        return super(SaleLine, self).default_get(fields,
+        return super(SaleLine, cls).default_get(fields,
             with_rec_name=with_rec_name)
 
-    def read(self, ids, fields_names=None):
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
+    @classmethod
+    def read(cls, ids, fields_names=None):
         if fields_names:
             fields_names2 = [x for x in fields_names
                     if not x.startswith('analytic_account_')]
         else:
             fields_names2 = fields_names
 
-        res = super(SaleLine, self).read(ids, fields_names=fields_names2)
+        res = super(SaleLine, cls).read(ids, fields_names=fields_names2)
 
         if not fields_names:
-            fields_names = list(set(self._columns.keys() \
-                    + self._inherit_fields.keys()))
+            fields_names = list(set(cls._fields.keys()
+                    + cls._inherit_fields.keys()))
 
         root_ids = []
         for field in fields_names:
@@ -66,7 +67,7 @@
             id2record = {}
             for record in res:
                 id2record[record['id']] = record
-            lines = self.browse(ids)
+            lines = cls.browse(ids)
             for line in lines:
                 for root_id in root_ids:
                     id2record[line.id]['analytic_account_' \
@@ -84,12 +85,11 @@
                                     str(account.root.id) + '.'):
                                 ham, field2 = field.split('.', 1)
                                 id2record[line.id][field] = account[field2]
-        if int_id:
-            return res[0]
         return res
 
-    def create(self, vals):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def create(cls, vals):
+        Selection = Pool().get('analytic_account.account.selection')
         vals = vals.copy()
         selection_vals = {}
         for field in vals.keys():
@@ -99,17 +99,17 @@
                     selection_vals['accounts'].append(('add', vals[field]))
                 del vals[field]
         if vals.get('analytic_accounts'):
-            selection_obj.write(vals['analytic_accounts'], selection_vals)
+            Selection.write([Selection(vals['analytic_accounts'])],
+                selection_vals)
         elif vals.get('type', 'line') == 'line':
-            selection_id = selection_obj.create(selection_vals)
-            vals['analytic_accounts'] = selection_id
-        return super(SaleLine, self).create(vals)
+            selection = Selection.create(selection_vals)
+            vals['analytic_accounts'] = selection.id
+        return super(SaleLine, cls).create(vals)
 
-    def write(self, ids, vals):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def write(cls, lines, vals):
+        Selection = Pool().get('analytic_account.account.selection')
         vals = vals.copy()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
         selection_vals = {}
         for field in vals.keys():
             if field.startswith('analytic_account_'):
@@ -117,7 +117,6 @@
                 selection_vals[root_id] = vals[field]
                 del vals[field]
         if selection_vals:
-            lines = self.browse(ids)
             for line in lines:
                 if line.type != 'line':
                     continue
@@ -125,11 +124,10 @@
                 if not line.analytic_accounts:
                     # Create missing selection
                     with Transaction().set_user(0):
-                        selection_id = selection_obj.create({})
-                    self.write(line.id, {
-                        'analytic_accounts': selection_id,
+                        selection = Selection.create({})
+                    cls.write([line], {
+                        'analytic_accounts': selection.id,
                         })
-                    line = self.browse(line.id)
                 for account in line.analytic_accounts.accounts:
                     if account.root.id in selection_vals:
                         value = selection_vals[account.root.id]
@@ -141,88 +139,73 @@
                     if account_id \
                             and account_id not in accounts:
                         accounts.append(account_id)
-                selection_obj.write(line.analytic_accounts.id, {
+                Selection.write([line.analytic_accounts], {
                     'accounts': [('set', accounts)],
                     })
-        return super(SaleLine, self).write(ids, vals)
+        return super(SaleLine, cls).write(lines, vals)
 
-    def delete(self, ids):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def delete(cls, lines):
+        Selection = Pool().get('analytic_account.account.selection')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        selection_ids = []
-        lines = self.browse(ids)
+        selections = []
         for line in lines:
             if line.analytic_accounts:
-                selection_ids.append(line.analytic_accounts.id)
+                selections.append(line.analytic_accounts)
 
-        res = super(SaleLine, self).delete(ids)
-        selection_obj.delete(selection_ids)
-        return res
+        super(SaleLine, cls).delete(lines)
+        Selection.delete(selections)
 
-    def copy(self, ids, default=None):
-        selection_obj = Pool().get('analytic_account.account.selection')
+    @classmethod
+    def copy(cls, lines, default=None):
+        Selection = Pool().get('analytic_account.account.selection')
 
-        new_ids = super(SaleLine, self).copy(ids, default=default)
+        new_lines = super(SaleLine, cls).copy(lines, default=default)
 
-        int_id = False
-        if isinstance(new_ids, (int, long)):
-            int_id = True
-            new_ids = [new_ids]
+        for line in new_lines:
+            if line.analytic_accounts:
+                selection, = Selection.copy([line.analytic_accounts])
+                cls.write([line], {
+                    'analytic_accounts': selection.id,
+                    })
+        return new_lines
 
-        for line in self.browse(new_ids):
-            if line.analytic_accounts:
-                selection_id = selection_obj.copy(line.analytic_accounts.id)
-                self.write(line.id, {
-                    'analytic_accounts': selection_id,
-                    })
+    def get_invoice_line(self, invoice_type):
+        AccountSelection = Pool().get('analytic_account.account.selection')
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
+        invoice_lines = super(SaleLine, self).get_invoice_line(invoice_type)
+        if not invoice_lines:
+            return invoice_lines
 
-    def get_invoice_line(self, line, invoice_type):
-        pool = Pool()
-        account_selection_obj = pool.get('analytic_account.account.selection')
+        selection = None
+        if self.analytic_accounts:
+            selection, = AccountSelection.copy([self.analytic_accounts])
+        for invoice_line in invoice_lines:
+            invoice_line.analytic_accounts = selection
+        return invoice_lines
 
-        res = super(SaleLine, self).get_invoice_line(line, invoice_type)
-        if not res:
-            return res
 
-        selection_id = False
-        if line.analytic_accounts:
-            selection_id = account_selection_obj.copy(
-                line.analytic_accounts.id)
-        for vals in res:
-            vals['analytic_accounts'] = selection_id
-        return res
+class Account:
+    __name__ = 'analytic_account.account'
 
-SaleLine()
+    @classmethod
+    def delete(cls, accounts):
+        SaleLine = Pool().get('sale.line')
+        super(Account, cls).delete(accounts)
+        # Restart the cache on the fields_view_get method of sale.line
+        SaleLine._fields_view_get_cache.clear()
 
+    @classmethod
+    def create(cls, vals):
+        SaleLine = Pool().get('sale.line')
+        account = super(Account, cls).create(vals)
+        # Restart the cache on the fields_view_get method of sale.line
+        SaleLine._fields_view_get_cache.clear()
+        return account
 
-class Account(ModelSQL, ModelView):
-    _name = 'analytic_account.account'
-
-    def delete(self, ids):
-        sale_line_obj = Pool().get('sale.line')
-        res = super(Account, self).delete(ids)
+    @classmethod
+    def write(cls, accounts, vals):
+        SaleLine = Pool().get('sale.line')
+        super(Account, cls).write(accounts, vals)
         # Restart the cache on the fields_view_get method of sale.line
-        sale_line_obj.fields_view_get.reset()
-        return res
-
-    def create(self, vals):
-        sale_line_obj = Pool().get('sale.line')
-        res = super(Account, self).create(vals)
-        # Restart the cache on the fields_view_get method of sale.line
-        sale_line_obj.fields_view_get.reset()
-        return res
-
-    def write(self, ids, vals):
-        sale_line_obj = Pool().get('sale.line')
-        res = super(Account, self).write(ids, vals)
-        # Restart the cache on the fields_view_get method of sale.line
-        sale_line_obj.fields_view_get.reset()
-        return res
-
-Account()
+        SaleLine._fields_view_get_cache.clear()
diff -r ec894ddf18f6 trytond/modules/calendar/__init__.py
--- a/trytond/modules/calendar/__init__.py	Wed Jul 25 20:55:21 2012 +0200
+++ b/trytond/modules/calendar/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,7 +1,32 @@
 #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.pool import Pool
 from . import caldav
 from .webdav import *
 from .calendar_ import *
 from .res import *
+
+
+def register():
+    Pool.register(
+        Collection,
+        Calendar,
+        ReadUser,
+        WriteUser,
+        Category,
+        Location,
+        Event,
+        EventCategory,
+        Alarm,
+        EventAlarm,
+        Attendee,
+        EventAttendee,
+        Date,
+        EventRDate,
+        EventExDate,
+        RRule,
+        EventRRule,
+        EventExRule,
+        User,
+        module='calendar', type_='model')
diff -r ec894ddf18f6 trytond/modules/calendar/caldav.py
--- a/trytond/modules/calendar/caldav.py	Wed Jul 25 20:55:21 2012 +0200
+++ b/trytond/modules/calendar/caldav.py	Mon Sep 24 20:36:40 2012 +0200
@@ -59,11 +59,11 @@
         raise DAV_NotFound
     pool = Pool(Transaction().cursor.database_name)
     try:
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
     except KeyError:
         raise DAV_NotFound
     try:
-        res = collection_obj.get_calendar_description(dburi, cache=CACHE)
+        res = Collection.get_calendar_description(dburi, cache=CACHE)
     except AttributeError:
         raise DAV_NotFound
     except DAV_Error, exception:
@@ -84,11 +84,11 @@
         raise DAV_NotFound
     pool = Pool(Transaction().cursor.database_name)
     try:
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
     except KeyError:
         raise DAV_NotFound
     try:
-        res = collection_obj.get_calendar_data(dburi, cache=CACHE)
+        res = Collection.get_calendar_data(dburi, cache=CACHE)
     except AttributeError:
         raise DAV_NotFound
     except DAV_Error, exception:
@@ -108,11 +108,11 @@
         raise DAV_NotFound
     pool = Pool(Transaction().cursor.database_name)
     try:
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
     except KeyError:
         raise DAV_NotFound
     try:
-        res = collection_obj.get_calendar_home_set(dburi, cache=CACHE)
+        res = Collection.get_calendar_home_set(dburi, cache=CACHE)
     except AttributeError:
         raise DAV_NotFound
     except DAV_Error, exception:
@@ -142,11 +142,11 @@
         raise DAV_NotFound
     pool = Pool(Transaction().cursor.database_name)
     try:
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
     except KeyError:
         raise DAV_NotFound
     try:
-        res = collection_obj.get_calendar_user_address_set(dburi, cache=CACHE)
+        res = Collection.get_calendar_user_address_set(dburi, cache=CACHE)
     except AttributeError:
         raise DAV_NotFound
     except DAV_Error, exception:
@@ -172,11 +172,11 @@
         raise DAV_NotFound
     pool = Pool(Transaction().cursor.database_name)
     try:
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
     except KeyError:
         raise DAV_NotFound
     try:
-        res = collection_obj.get_schedule_inbox_URL(dburi, cache=CACHE)
+        res = Collection.get_schedule_inbox_URL(dburi, cache=CACHE)
     except AttributeError:
         raise DAV_NotFound
     except DAV_Error, exception:
@@ -204,11 +204,11 @@
         raise DAV_NotFound
     pool = Pool(Transaction().cursor.database_name)
     try:
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
     except KeyError:
         raise DAV_NotFound
     try:
-        res = collection_obj.get_schedule_outbox_URL(dburi, cache=CACHE)
+        res = Collection.get_schedule_outbox_URL(dburi, cache=CACHE)
     except AttributeError:
         raise DAV_NotFound
     except DAV_Error, exception:
@@ -258,9 +258,9 @@
     if not dbname:
         raise DAV_Forbidden
     pool = Pool(Transaction().cursor.database_name)
-    calendar_obj = pool.get('calendar.calendar')
+    Calendar = pool.get('calendar.calendar')
     try:
-        res = calendar_obj.post(dburi, body)
+        res = Calendar.post(dburi, body)
     except AttributeError:
         raise DAV_NotFound
     except DAV_Error, exception:
diff -r ec894ddf18f6 trytond/modules/calendar/calendar_.py
--- a/trytond/modules/calendar/calendar_.py	Wed Jul 25 20:55:21 2012 +0200
+++ b/trytond/modules/calendar/calendar_.py	Mon Sep 24 20:36:40 2012 +0200
@@ -13,6 +13,12 @@
 from trytond.transaction import Transaction
 from trytond.cache import Cache
 from trytond.pool import Pool
+
+__all__ = ['Calendar', 'ReadUser', 'WriteUser', 'Category', 'Location',
+    'Event', 'EventCategory', 'Alarm', 'EventAlarm', 'Attendee',
+    'EventAttendee', 'Date', 'EventRDate', 'EventExDate', 'RRule',
+    'EventRRule', 'EventExRule']
+
 tzlocal = dateutil.tz.tzlocal()
 tzutc = dateutil.tz.tzutc()
 domimpl = xml.dom.minidom.getDOMImplementation()
@@ -20,9 +26,7 @@
 
 class Calendar(ModelSQL, ModelView):
     "Calendar"
-    _description = __doc__
-    _name = 'calendar.calendar'
-
+    __name__ = 'calendar.calendar'
     name = fields.Char('Name', required=True, select=True)
     description = fields.Text('Description')
     owner = fields.Many2One('res.user', 'Owner', select=True,
@@ -32,95 +36,96 @@
             'calendar', 'user', 'Read Users')
     write_users = fields.Many2Many('calendar.calendar-write-res.user',
             'calendar', 'user', 'Write Users')
+    _get_name_cache = Cache('calendar_calendar.get_name')
 
-    def __init__(self):
-        super(Calendar, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Calendar, cls).__setup__()
+        cls._sql_constraints = [
             ('name_uniq', 'UNIQUE(name)',
                 'The name of calendar must be unique!'),
             ('owner_uniq', 'UNIQUE(owner)',
                 'A user can have only one calendar!'),
-        ]
-        self._order.insert(0, ('name', 'ASC'))
-        self._constraints += [
+            ]
+        cls._order.insert(0, ('name', 'ASC'))
+        cls._constraints += [
             ('check_name', 'Calendar name can not end with .ics'),
-        ]
+            ]
 
-    def create(self, vals):
-        res = super(Calendar, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        calendar = super(Calendar, cls).create(vals)
         # Restart the cache for get_name
-        self.get_name.reset()
-        return res
+        cls._get_name_cache.reset()
+        return calendar
 
-    def write(self, ids, vals):
-        res = super(Calendar, self).write(ids, vals)
+    @classmethod
+    def write(cls, calendars, vals):
+        super(Calendar, cls).write(calendars, vals)
         # Restart the cache for get_name
-        self.get_name.reset()
-        return res
+        cls._get_name_cache.reset()
 
-    def delete(self, ids):
-        res = super(Calendar, self).delete(ids)
+    @classmethod
+    def delete(cls, calendars):
+        super(Calendar, cls).delete(calendars)
         # Restart the cache for calendar
-        self.get_name.reset()
-        return res
+        cls._get_name_cache.reset()
 
-    def check_name(self, ids):
+    def check_name(self):
         '''
         Check the name doesn't end with .ics
         '''
-        for calendar in self.browse(ids):
-            if calendar.name.endswith('.ics'):
-                return False
+        if self.name.endswith('.ics'):
+            return False
         return True
 
-    @Cache('calendar_calendar.get_name')
-    def get_name(self, name):
+    @classmethod
+    def get_name(cls, name):
         '''
         Return the calendar id of the name
+        '''
+        calendar_id = cls._get_name_cache.get(name, default=-1)
+        if calendar_id == -1:
+            calendars = cls.search([
+                ('name', '=', name),
+                ], limit=1)
+            if calendars:
+                calendar_id = calendars[0].id
+            else:
+                calendar_id = None
+            cls._get_name_cache.set(name, calendar_id)
+        return calendar_id
 
-        :param name: the calendar name
-        :return: the calendar.calendar id
-        '''
-        calendar_ids = self.search([
-            ('name', '=', name),
-            ], limit=1)
-        if calendar_ids:
-            return calendar_ids[0]
-
-    def calendar2ical(self, calendar_id):
+    @staticmethod
+    def calendar2ical(calendar_id):
         '''
         Return an iCalendar object for the given calendar_id containing
         all the vevent objects
-
-        :param calendar_id: an id of calendar.calendar
-        :return: an iCalendar
         '''
-        event_obj = Pool().get('calendar.event')
+        Event = Pool().get('calendar.event')
 
         ical = vobject.iCalendar()
         ical.vevent_list = []
-        event_ids = event_obj.search([
-            ('calendar', '=', calendar_id),
-            ('parent', '=', None),
-            ])
-        for event in event_obj.browse(event_ids):
-            ical2 = event_obj.event2ical(event.id)
+        events = Event.search([
+                ('calendar', '=', calendar_id),
+                ('parent', '=', None),
+                ])
+        for event in events:
+            ical2 = event.event2ical()
             ical.vevent_list.extend(ical2.vevent_list)
         return ical
 
-    def _fbtype(self, event):
+    @property
+    def _fbtype(self):
         '''
         Return the freebusy type for give transparent and status
-
-        :param event: a BrowseRecord of calendar.event
-        :return: a freebusy type ('FREE', 'BUSY', 'BUSY-TENTATIVE')
         '''
-        if event.transp == 'opaque':
-            if not event.status or event.status == 'confirmed':
+        if self.transp == 'opaque':
+            if not self.status or self.status == 'confirmed':
                 fbtype = 'BUSY'
-            elif event.status == 'cancelled':
+            elif self.status == 'cancelled':
                 fbtype = 'FREE'
-            elif event.status == 'tentative':
+            elif self.status == 'tentative':
                 fbtype = 'BUSY-TENTATIVE'
             else:
                 fbtype = 'BUSY'
@@ -128,17 +133,13 @@
             fbtype = 'FREE'
         return fbtype
 
-    def freebusy(self, calendar_id, dtstart, dtend):
+    @classmethod
+    def freebusy(cls, calendar_id, dtstart, dtend):
         '''
         Return an iCalendar object for the given calendar_id with the
         vfreebusy objects between the two dates
-
-        :param calendar_id: an id of calendar.calendar
-        :param dtstart: a date or datetime
-        :param dtend: a date of datetime
-        :return: an iCalendar
         '''
-        event_obj = Pool().get('calendar.event')
+        Event = Pool().get('calendar.event')
 
         ical = vobject.iCalendar()
         ical.add('method').value = 'REPLY'
@@ -157,31 +158,30 @@
             ical.vfreebusy.add('dtend').value = dtend.astimezone(tzutc)
 
         with Transaction().set_user(0):
-            event_ids = event_obj.search([
-                ['OR',
-                    [('dtstart', '<=', dtstart),
-                        ('dtend', '>=', dtstart)],
-                    [('dtstart', '<=', dtend),
-                        ('dtend', '>=', dtend)],
-                    [('dtstart', '>=', dtstart),
-                        ('dtend', '<=', dtend)],
-                    [('dtstart', '>=', dtstart),
-                        ('dtstart', '<=', dtend),
-                        ('dtend', '=', None)]],
-                ('parent', '=', None),
-                ('rdates', '=', None),
-                ('rrules', '=', None),
-                ('exdates', '=', None),
-                ('exrules', '=', None),
-                ('occurences', '=', None),
-                ('calendar', '=', calendar_id),
-                ])
-            events = event_obj.browse(event_ids)
+            events = Event.search([
+                    ['OR',
+                        [('dtstart', '<=', dtstart),
+                            ('dtend', '>=', dtstart)],
+                        [('dtstart', '<=', dtend),
+                            ('dtend', '>=', dtend)],
+                        [('dtstart', '>=', dtstart),
+                            ('dtend', '<=', dtend)],
+                        [('dtstart', '>=', dtstart),
+                            ('dtstart', '<=', dtend),
+                            ('dtend', '=', None)]],
+                    ('parent', '=', None),
+                    ('rdates', '=', None),
+                    ('rrules', '=', None),
+                    ('exdates', '=', None),
+                    ('exrules', '=', None),
+                    ('occurences', '=', None),
+                    ('calendar', '=', calendar_id),
+                    ])
 
         for event in events:
             # Don't group freebusy as sunbird doesn't handle it
             freebusy = ical.vfreebusy.add('freebusy')
-            freebusy.fbtype_param = self._fbtype(event)
+            freebusy.fbtype_param = event._fbtype
             if event.dtstart.replace(tzinfo=tzlocal) >= dtstart:
                 freebusy_dtstart = event.dtstart.replace(tzinfo=tzlocal)
             else:
@@ -195,22 +195,21 @@
                 freebusy_dtend.astimezone(tzutc))]
 
         with Transaction().set_user(0):
-            event_ids = event_obj.search([
-                ('parent', '=', None),
-                ('dtstart', '<=', dtend),
-                ['OR',
-                    ('rdates', '!=', None),
-                    ('rrules', '!=', None),
-                    ('exdates', '!=', None),
-                    ('exrules', '!=', None),
-                    ('occurences', '!=', None),
-                ],
-                ('calendar', '=', calendar_id),
-                ])
-            events = event_obj.browse(event_ids)
+            events = Event.search([
+                    ('parent', '=', None),
+                    ('dtstart', '<=', dtend),
+                    ['OR',
+                        ('rdates', '!=', None),
+                        ('rrules', '!=', None),
+                        ('exdates', '!=', None),
+                        ('exrules', '!=', None),
+                        ('occurences', '!=', None),
+                        ],
+                    ('calendar', '=', calendar_id),
+                    ])
 
         for event in events:
-            event_ical = event_obj.event2ical(event)
+            event_ical = event.event2ical()
             if event_ical.vevent.rruleset:
                 between_dtstart, between_dtend = dtstart, dtend
                 for freebusy_dtstart in event_ical.vevent.rruleset:
@@ -231,7 +230,7 @@
                             or (f_dtstart_tz >= dtstart
                                 and f_dtend_tz <= dtend)):
                         continue
-                    freebusy_fbtype = self._fbtype(event)
+                    freebusy_fbtype = event._fbtype
                     all_day = event.all_day
                     for occurence in event.occurences:
                         if (occurence.recurrence.replace(tzinfo=tzlocal)
@@ -244,7 +243,7 @@
                             else:
                                 freebusy_dtend = freebusy_dtstart
                             all_day = occurence.all_day
-                            freebusy_fbtype = self._fbtype(occurence)
+                            freebusy_fbtype = occurence._fbtype
                             break
                     freebusy = ical.vfreebusy.add('freebusy')
                     freebusy.fbtype_param = freebusy_fbtype
@@ -263,21 +262,19 @@
                             freebusy_dtend.astimezone(tzutc))]
         return ical
 
-    def post(self, uri, data):
+    @classmethod
+    def post(cls, uri, data):
         '''
-        Handle post of vfreebusy request
-
-        :param uri: the posted uri
-        :param data: the posted data
-        :return: the xml with schedule-response
+        Handle post of vfreebusy request and return the XML with
+        schedule-response
         '''
         from pywebdav.lib.errors import DAV_Forbidden
-        collection_obj = Pool().get('webdav.collection')
+        Collection = Pool().get('webdav.collection')
 
-        calendar_id = collection_obj.calendar(uri)
+        calendar_id = Collection.calendar(uri)
         if not calendar_id:
             raise DAV_Forbidden
-        calendar = self.browse(calendar_id)
+        calendar = cls(calendar_id)
         if calendar.owner.id != Transaction().user:
             raise DAV_Forbidden
         ical = vobject.readOne(data)
@@ -318,11 +315,11 @@
                 if attendee.value.lower().startswith('mailto:'):
                     email = attendee.value[7:]
                 with Transaction().set_user(0):
-                    calendar_ids = self.search([
-                        ('owner.email', '=', email),
-                        ])
-                if calendar_ids:
-                    vfreebusy = self.freebusy(calendar_ids[0], dtstart, dtend)
+                    calendars = cls.search([
+                            ('owner.email', '=', email),
+                            ])
+                if calendars:
+                    vfreebusy = cls.freebusy(calendars[0].id, dtstart, dtend)
                     vfreebusy.vfreebusy.add('dtstamp').value = \
                             ical.vfreebusy.dtstamp.value
                     vfreebusy.vfreebusy.add('uid').value = \
@@ -343,77 +340,59 @@
             return doc.toxml(encoding='utf-8')
         raise DAV_Forbidden
 
-Calendar()
-
 
 class ReadUser(ModelSQL):
     'Calendar - read - User'
-    _description = __doc__
-    _name = 'calendar.calendar-read-res.user'
-
+    __name__ = 'calendar.calendar-read-res.user'
     calendar = fields.Many2One('calendar.calendar', 'Calendar',
             ondelete='CASCADE', required=True, select=True)
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE',
             required=True, select=True)
 
-ReadUser()
-
 
 class WriteUser(ModelSQL):
     'Calendar - write - User'
-    _description = __doc__
-    _name = 'calendar.calendar-write-res.user'
-
+    __name__ = 'calendar.calendar-write-res.user'
     calendar = fields.Many2One('calendar.calendar', 'Calendar',
             ondelete='CASCADE', required=True, select=True)
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE',
             required=True, select=True)
 
-WriteUser()
-
 
 class Category(ModelSQL, ModelView):
     "Category"
-    _description = __doc__
-    _name = 'calendar.category'
-
+    __name__ = 'calendar.category'
     name = fields.Char('Name', required=True, select=True)
 
-    def __init__(self):
-        super(Category, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Category, cls).__setup__()
+        cls._sql_constraints = [
             ('name_uniq', 'UNIQUE(name)',
                 'The name of calendar category must be unique!'),
-        ]
-        self._order.insert(0, ('name', 'ASC'))
-
-Category()
+            ]
+        cls._order.insert(0, ('name', 'ASC'))
 
 
 class Location(ModelSQL, ModelView):
     "Location"
-    _description = __doc__
-    _name = 'calendar.location'
-
+    __name__ = 'calendar.location'
     name = fields.Char('Name', required=True, select=True)
 
-    def __init__(self):
-        super(Location, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Location, cls).__setup__()
+        cls._sql_constraints = [
             ('name_uniq', 'UNIQUE(name)',
                 'The name of calendar location must be unique!'),
-        ]
-        self._order.insert(0, ('name', 'ASC'))
-
-Location()
+            ]
+        cls._order.insert(0, ('name', 'ASC'))
 
 
 class Event(ModelSQL, ModelView):
     "Event"
-    _description = __doc__
-    _name = 'calendar.event'
+    __name__ = 'calendar.event'
     _rec_name = 'uuid'
-
     uuid = fields.Char('UUID', required=True,
             help='Universally Unique Identifier', select=True)
     calendar = fields.Many2One('calendar.calendar', 'Calendar',
@@ -496,90 +475,95 @@
         'Write Users'), 'get_calendar_field', searcher='search_calendar_field')
     vevent = fields.Binary('vevent')
 
-    def __init__(self):
-        super(Event, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Event, cls).__setup__()
+        cls._sql_constraints = [
             ('uuid_recurrence_uniq', 'UNIQUE(uuid, calendar, recurrence)',
                 'UUID and recurrence must be unique in a calendar!'),
-        ]
-        self._constraints += [
+            ]
+        cls._constraints += [
             ('check_recurrence', 'invalid_recurrence'),
-        ]
-        self._error_messages.update({
-            'invalid_recurrence': 'Recurrence can not be recurrent!',
-        })
+            ]
+        cls._error_messages.update({
+                'invalid_recurrence': 'Recurrence can not be recurrent!',
+                })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         # Migrate from 1.4: remove classification_public
-        model_data_obj = Pool().get('ir.model.data')
-        rule_obj = Pool().get('ir.rule')
+        ModelData = Pool().get('ir.model.data')
+        Rule = Pool().get('ir.rule')
         with Transaction().set_user(0):
-            model_data_ids = model_data_obj.search([
-                ('fs_id', '=', 'rule_group_read_calendar_line3'),
-                ('module', '=', module_name),
-                ('inherit', '=', None),
-                ], limit=1)
-            if model_data_ids:
-                model_data = model_data_obj.browse(model_data_ids[0])
-                rule_obj.delete(model_data.db_id)
-        return super(Event, self).init(module_name)
+            models_data = ModelData.search([
+                    ('fs_id', '=', 'rule_group_read_calendar_line3'),
+                    ('module', '=', module_name),
+                    ('inherit', '=', None),
+                    ], limit=1)
+            if models_data:
+                model_data, = models_data
+                Rule.delete([Rule(model_data.db_id)])
+        return super(Event, cls).__register__(module_name)
 
-    def default_uuid(self):
+    @staticmethod
+    def default_uuid():
         return str(uuid.uuid4())
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return 0
 
-    def default_classification(self):
+    @staticmethod
+    def default_classification():
         return 'public'
 
-    def default_transp(self):
+    @staticmethod
+    def default_transp():
         return 'opaque'
 
-    def default_timezone(self):
-        user_obj = Pool().get('res.user')
-        user = user_obj.browse(Transaction().user)
+    @staticmethod
+    def default_timezone():
+        User = Pool().get('res.user')
+        user = User(Transaction().user)
         return user.timezone
 
-    def timezones(self):
+    @staticmethod
+    def timezones():
         return [(x, x) for x in pytz.common_timezones] + [('', '')]
 
-    def get_calendar_field(self, ids, name):
+    def get_calendar_field(self, name):
         assert name in ('calendar_owner', 'calendar_read_users',
                 'calendar_write_users'), 'Invalid name'
-        res = {}
         name = name[9:]
-        for event in self.browse(ids):
-            if name in ('read_users', 'write_users'):
-                res[event.id] = [x.id for x in event.calendar[name]]
-            else:
-                res[event.id] = event.calendar[name].id
-        return res
+        if name in ('read_users', 'write_users'):
+            return [x.id for x in getattr(self.calendar, name)]
+        else:
+            return getattr(self.calendar, name).id
 
-    def search_calendar_field(self, name, clause):
+    @classmethod
+    def search_calendar_field(cls, name, clause):
         return [('calendar.' + name[9:],) + clause[1:]]
 
-    def check_recurrence(self, ids):
+    def check_recurrence(self):
         '''
         Check the recurrence is not recurrent.
         '''
-        for event in self.browse(ids):
-            if not event.parent:
-                continue
-            if event.rdates \
-                    or event.rrules \
-                    or event.exdates \
-                    or event.exrules \
-                    or event.occurences:
+        if self.parent:
+            if self.rdates \
+                    or self.rrules \
+                    or self.exdates \
+                    or self.exrules \
+                    or self.occurences:
                 return False
         return True
 
-    def create(self, values):
-        calendar_obj = Pool().get('calendar.calendar')
-        collection_obj = Pool().get('webdav.collection')
+    @classmethod
+    def create(cls, values):
+        pool = Pool()
+        Calendar = pool.get('calendar.calendar')
+        Collection = pool.get('webdav.collection')
 
-        res = super(Event, self).create(values)
-        event = self.browse(res)
+        event = super(Event, cls).create(values)
         if (event.calendar.owner
                 and (event.organizer == event.calendar.owner.email
                     or (event.parent
@@ -595,94 +579,80 @@
                         and x.email != event.parent.organizer]
             if attendee_emails:
                 with Transaction().set_user(0):
-                    calendar_ids = calendar_obj.search([
-                        ('owner.email', 'in', attendee_emails),
-                        ])
+                    calendars = Calendar.search([
+                            ('owner.email', 'in', attendee_emails),
+                            ])
                     if not event.recurrence:
-                        for calendar_id in calendar_ids:
-                            new_id = self.copy(event.id, default={
-                                'calendar': calendar_id,
-                                'occurences': None,
-                                })
+                        for calendar in calendars:
+                            new_event, = cls.copy([event], default={
+                                    'calendar': calendar.id,
+                                    'occurences': None,
+                                    })
                             for occurence in event.occurences:
-                                self.copy(occurence.id, default={
-                                    'calendar': calendar_id,
-                                    'parent': new_id,
+                                cls.copy([occurence], default={
+                                    'calendar': calendar.id,
+                                    'parent': new_event.id,
                                     })
                     else:
-                        parent_ids = self.search([
-                            ('uuid', '=', event.uuid),
-                            ('calendar.owner.email', 'in', attendee_emails),
-                            ('id', '!=', event.id),
-                            ('recurrence', '=', None),
-                            ])
-                        for parent in self.browse(parent_ids):
-                            self.copy(event.id, default={
-                                'calendar': parent.calendar.id,
-                                'parent': parent.id,
-                                })
+                        parents = cls.search([
+                                ('uuid', '=', event.uuid),
+                                ('calendar.owner.email', 'in',
+                                    attendee_emails),
+                                ('id', '!=', event.id),
+                                ('recurrence', '=', None),
+                                ])
+                        for parent in parents:
+                            cls.copy([event], default={
+                                    'calendar': parent.calendar.id,
+                                    'parent': parent.id,
+                                    })
         # Restart the cache for event
-        collection_obj.event.reset()
-        return res
+        Collection._event_cache.reset()
+        return event
 
-    def _event2update(self, event):
+    def _event2update(self):
+        return {
+            'summary': self.summary,
+            'description': self.description,
+            'all_day': self.all_day,
+            'dtstart': self.dtstart,
+            'dtend': self.dtend,
+            'location': self.location.id,
+            'status': self.status,
+            'organizer': self.organizer,
+            'rdates': [('delete_all',)] + [('create', rdate._date2update())
+                for rdate in self.rdates],
+            'exdates': [('delete_all',)] + [('create', exdate._date2update())
+                for exdate in self.exdates],
+            'rrules': [('delete_all',)] + [('create', rrule._date2update())
+                for rrule in self.rrules],
+            'exrules': [('delete_all',)] + [('create', exrule._date2update())
+                for exrule in self.exrules],
+            }
+
+    @classmethod
+    def write(cls, events, values):
         pool = Pool()
-        rdate_obj = pool.get('calendar.event.rdate')
-        exdate_obj = pool.get('calendar.event.exdate')
-        rrule_obj = pool.get('calendar.event.rrule')
-        exrule_obj = pool.get('calendar.event.exrule')
-
-        res = {}
-        res['summary'] = event.summary
-        res['description'] = event.description
-        res['all_day'] = event.all_day
-        res['dtstart'] = event.dtstart
-        res['dtend'] = event.dtend
-        res['location'] = event.location.id
-        res['status'] = event.status
-        res['organizer'] = event.organizer
-        res['rdates'] = [('delete_all',)]
-        for rdate in event.rdates:
-            vals = rdate_obj._date2update(rdate)
-            res['rdates'].append(('create', vals))
-        res['exdates'] = [('delete_all',)]
-        for exdate in event.exdates:
-            vals = exdate_obj._date2update(exdate)
-            res['exdates'].append(('create', vals))
-        res['rrules'] = [('delete_all',)]
-        for rrule in event.rrules:
-            vals = rrule_obj._rule2update(rrule)
-            res['rrules'].append(('create', vals))
-        res['exrules'] = [('delete_all',)]
-        for exrule in event.exrules:
-            vals = exrule_obj._rule2update(exrule)
-            res['exrules'].append(('create', vals))
-        return res
-
-    def write(self, ids, values):
-        calendar_obj = Pool().get('calendar.calendar')
-        collection_obj = Pool().get('webdav.collection')
+        Calendar = pool.get('calendar.calendar')
+        Collection = pool.get('webdav.collection')
         cursor = Transaction().cursor
 
         values = values.copy()
         if 'sequence' in values:
             del values['sequence']
 
-        res = super(Event, self).write(ids, values)
+        super(Event, cls).write(events, values)
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        for i in range(0, len(ids), cursor.IN_MAX):
-            sub_ids = ids[i:i + cursor.IN_MAX]
+        for i in range(0, len(events), cursor.IN_MAX):
+            sub_ids = map(int, events[i:i + cursor.IN_MAX])
             red_sql, red_ids = reduce_ids('id', sub_ids)
-            cursor.execute('UPDATE "' + self._table + '" ' \
-                    'SET sequence = sequence + 1 ' \
-                    'WHERE ' + red_sql, red_ids)
+            cursor.execute('UPDATE "' + cls._table + '" '
+                'SET sequence = sequence + 1 '
+                'WHERE ' + red_sql, red_ids)
 
         if not values:
-            return res
-        for event in self.browse(ids):
+            return
+        for event in events:
             if event.calendar.owner \
                     and (event.organizer == event.calendar.owner.email \
                     or (event.parent \
@@ -696,79 +666,72 @@
                             if x.status != 'declined'
                             and x.email != event.parent.organizer]
                 with Transaction().set_user(0):
-                    event_ids = self.search([
-                        ('uuid', '=', event.uuid),
-                        ('id', '!=', event.id),
-                        ('recurrence', '=', event.recurrence),
-                        ])
-                    for event2 in self.browse(event_ids):
+                    events2 = cls.search([
+                            ('uuid', '=', event.uuid),
+                            ('id', '!=', event.id),
+                            ('recurrence', '=', event.recurrence),
+                            ])
+                    for event2 in events2:
                         if event2.calendar.owner.email in attendee_emails:
                             attendee_emails.remove(
                                     event2.calendar.owner.email)
                         else:
-                            event_ids.remove(event2.id)
-                            self.delete(event2.id)
-                    if event_ids:
-                        self.write(event_ids, self._event2update(event))
+                            events2.remove(event2)
+                            cls.delete([event2])
+                    if events2:
+                        cls.write(events2, event._event2update())
                 if attendee_emails:
                     with Transaction().set_user(0):
-                        calendar_ids = calendar_obj.search([
-                            ('owner.email', 'in', attendee_emails),
-                            ])
+                        calendars = Calendar.search([
+                                ('owner.email', 'in', attendee_emails),
+                                ])
                         if not event.recurrence:
-                            for calendar_id in calendar_ids:
-                                new_id = self.copy(event.id, default={
-                                    'calendar': calendar_id,
-                                    'occurences': None,
-                                    })
+                            for calendar in calendars:
+                                new_event, = cls.copy([event], default={
+                                        'calendar': calendar.id,
+                                        'occurences': None,
+                                        })
                                 for occurence in event.occurences:
-                                    self.copy(occurence.id, default={
-                                        'calendar': calendar_id,
-                                        'parent': new_id,
-                                        })
+                                    cls.copy([occurence], default={
+                                            'calendar': calendar.id,
+                                            'parent': new_event.id,
+                                            })
                         else:
-                            parent_ids = self.search([
+                            parents = cls.search([
                                     ('uuid', '=', event.uuid),
                                     ('calendar.owner.email', 'in',
                                         attendee_emails),
                                     ('id', '!=', event.id),
                                     ('recurrence', '=', None),
                                     ])
-                            for parent in self.browse(parent_ids):
-                                self.copy(event.id, default={
-                                    'calendar': parent.calendar.id,
-                                    'parent': parent.id,
-                                    })
+                            for parent in parents:
+                                cls.copy([event], default={
+                                        'calendar': parent.calendar.id,
+                                        'parent': parent.id,
+                                        })
         # Restart the cache for event
-        collection_obj.event.reset()
-        return res
+        Collection._event_cache.reset()
 
-    def copy(self, ids, default=None):
-        int_id = isinstance(ids, (int, long))
-        if int_id:
-            ids = [ids]
-
+    @classmethod
+    def copy(cls, events, default=None):
         if default is None:
             default = {}
 
-        new_ids = []
-        for event_id in ids:
+        new_events = []
+        for event in events:
             current_default = default.copy()
-            current_default['uuid'] = self.default_uuid()
-            new_id = super(Event, self).copy(event_id, default=current_default)
-            new_ids.append(new_id)
+            current_default['uuid'] = cls.default_uuid()
+            new_events.append(super(Event, cls).copy([event],
+                    default=current_default))
+        return new_events
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
+    @classmethod
+    def delete(cls, events):
+        pool = Pool()
+        Attendee = pool.get('calendar.event.attendee')
+        Collection = pool.get('webdav.collection')
 
-    def delete(self, ids):
-        attendee_obj = Pool().get('calendar.event.attendee')
-        collection_obj = Pool().get('webdav.collection')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for event in self.browse(ids):
+        for event in events:
             if event.calendar.owner \
                     and (event.organizer == event.calendar.owner.email \
                     or (event.parent \
@@ -781,13 +744,13 @@
                             if x.email != event.parent.organizer]
                 if attendee_emails:
                     with Transaction().set_user(0):
-                        event_ids = self.search([
-                            ('uuid', '=', event.uuid),
-                            ('calendar.owner.email', 'in', attendee_emails),
-                            ('id', '!=', event.id),
-                            ('recurrence', '=', event.recurrence),
-                            ])
-                        self.delete(event_ids)
+                        cls.delete(cls.search([
+                                    ('uuid', '=', event.uuid),
+                                    ('calendar.owner.email', 'in',
+                                        attendee_emails),
+                                    ('id', '!=', event.id),
+                                    ('recurrence', '=', event.recurrence),
+                                    ]))
             elif event.organizer \
                     or (event.parent and event.parent.organizer):
                 if event.organizer:
@@ -795,43 +758,37 @@
                 else:
                     organizer = event.parent.organizer
                 with Transaction().set_user(0):
-                    event_ids = self.search([
-                        ('uuid', '=', event.uuid),
-                        ('calendar.owner.email', '=', organizer),
-                        ('id', '!=', event.id),
-                        ('recurrence', '=', event.recurrence),
-                        ], limit=1)
-                    if event_ids:
-                        event2 = self.browse(event_ids[0])
+                    events2 = cls.search([
+                            ('uuid', '=', event.uuid),
+                            ('calendar.owner.email', '=', organizer),
+                            ('id', '!=', event.id),
+                            ('recurrence', '=', event.recurrence),
+                            ], limit=1)
+                    if events2:
+                        event2, = events2
                         for attendee in event2.attendees:
                             if attendee.email == event.calendar.owner.email:
-                                attendee_obj.write(attendee.id, {
-                                    'status': 'declined',
-                                    })
-        res = super(Event, self).delete(ids)
+                                Attendee.write([attendee], {
+                                        'status': 'declined',
+                                        })
+        super(Event, cls).delete(events)
         # Restart the cache for event
-        collection_obj.event.reset()
-        return res
+        Collection._event_cache.reset()
 
-    def ical2values(self, event_id, ical, calendar_id, vevent=None):
+    @classmethod
+    def ical2values(cls, event_id, ical, calendar_id, vevent=None):
         '''
-        Convert iCalendar to values for create or write
-
-        :param event_id: the event id for write or None for create
-        :param ical: a ical instance of vobject
-        :param calendar_id: the calendar id of the event
-        :param vevent: the vevent of the ical to use if None use the first one
-        :return: a dictionary with values
+        Convert iCalendar to values for create or write with
+        the event id for write or None for create
         '''
         pool = Pool()
-        category_obj = pool.get('calendar.category')
-        location_obj = pool.get('calendar.location')
-        alarm_obj = pool.get('calendar.event.alarm')
-        attendee_obj = pool.get('calendar.event.attendee')
-        rdate_obj = pool.get('calendar.event.rdate')
-        exdate_obj = pool.get('calendar.event.exdate')
-        rrule_obj = pool.get('calendar.event.rrule')
-        exrule_obj = pool.get('calendar.event.exrule')
+        Category = pool.get('calendar.category')
+        Location = pool.get('calendar.location')
+        Rdate = pool.get('calendar.event.rdate')
+        Exdate = pool.get('calendar.event.exdate')
+        Rrule = pool.get('calendar.rrule')
+        Exrule = pool.get('calendar.event.exrule')
+        Valarm = pool.get('calendar.event.valarm')
 
         vevents = []
         if not vevent:
@@ -844,7 +801,7 @@
 
         event = None
         if event_id:
-            event = self.browse(event_id)
+            event = cls(event_id)
 
         res = {}
         if not event:
@@ -902,24 +859,23 @@
             res['status'] = ''
         if hasattr(vevent, 'categories'):
             with Transaction().set_context(active_test=False):
-                category_ids = category_obj.search([
-                    ('name', 'in', [x for x in vevent.categories.value]),
-                    ])
-            categories = category_obj.browse(category_ids)
+                categories = Category.search([
+                        ('name', 'in', [x for x in vevent.categories.value]),
+                        ])
             category_names2ids = {}
             for category in categories:
                 category_names2ids[category.name] = category.id
             for category in vevent.categories.value:
                 if category not in category_names2ids:
-                    category_ids.append(category_obj.create({
-                        'name': category,
-                        }))
-            res['categories'] = [('set', category_ids)]
+                    categories.append(Category.create({
+                                'name': category,
+                                }))
+            res['categories'] = [('set', map(int, categories))]
         else:
             res['categories'] = [('unlink_all',)]
         if hasattr(vevent, 'class'):
             if getattr(vevent, 'class').value.lower() in \
-                    dict(self.classification.selection):
+                    dict(cls.classification.selection):
                 res['classification'] = getattr(vevent, 'class').value.lower()
             else:
                 res['classification'] = 'public'
@@ -927,16 +883,16 @@
             res['classification'] = 'public'
         if hasattr(vevent, 'location'):
             with Transaction().set_context(active_test=False):
-                location_ids = location_obj.search([
-                    ('name', '=', vevent.location.value),
-                    ], limit=1)
-            if not location_ids:
-                location_id = location_obj.create({
-                    'name': vevent.location.value,
-                    })
+                locations = Location.search([
+                        ('name', '=', vevent.location.value),
+                        ], limit=1)
+            if not locations:
+                location = Location.create({
+                        'name': vevent.location.value,
+                        })
             else:
-                location_id = location_ids[0]
-            res['location'] = location_id
+                location, = locations
+            res['location'] = location.id
         else:
             res['location'] = None
 
@@ -963,7 +919,7 @@
         if hasattr(vevent, 'attendee'):
             while vevent.attendee_list:
                 attendee = vevent.attendee_list.pop()
-                vals = attendee_obj.attendee2values(attendee)
+                vals = Attendee.attendee2values(attendee)
                 if vals['email'] in attendees_todel:
                     res['attendees'].append(('write',
                         attendees_todel[vals['email']], vals))
@@ -978,9 +934,8 @@
         if hasattr(vevent, 'rdate'):
             while vevent.rdate_list:
                 rdate = vevent.rdate_list.pop()
-                for date in rdate.value:
-                    vals = rdate_obj.date2values(date)
-                    res['rdates'].append(('create', vals))
+                for rdate in rdate.value:
+                    res['rdates'].append(('create', Rdate.date2values(rdate)))
 
         res['exdates'] = []
         if event:
@@ -988,9 +943,9 @@
         if hasattr(vevent, 'exdate'):
             while vevent.exdate_list:
                 exdate = vevent.exdate_list.pop()
-                for date in exdate.value:
-                    vals = exdate_obj.date2values(date)
-                    res['exdates'].append(('create', vals))
+                for exdate in exdate.value:
+                    res['exdates'].append(('create',
+                            Exdate.date2values(exdate)))
 
         res['rrules'] = []
         if event:
@@ -998,8 +953,7 @@
         if hasattr(vevent, 'rrule'):
             while vevent.rrule_list:
                 rrule = vevent.rrule_list.pop()
-                vals = rrule_obj.rule2values(rrule)
-                res['rrules'].append(('create', vals))
+                res['rrules'].append(('create', Rrule.rule2values(rrule)))
 
         res['exrules'] = []
         if event:
@@ -1007,8 +961,7 @@
         if hasattr(vevent, 'exrule'):
             while vevent.exrule_list:
                 exrule = vevent.exrule_list.pop()
-                vals = exrule_obj.rule2values(exrule)
-                res['exrules'].append(('create', vals))
+                res['exrules'].append(('create', Exrule.rule2values(exrule)))
 
         if event:
             res.setdefault('alarms', [])
@@ -1017,8 +970,7 @@
             res.setdefault('alarms', [])
             while vevent.valarm_list:
                 valarm = vevent.valarm_list.pop()
-                vals = alarm_obj.valarm2values(valarm)
-                res['alarms'].append(('create', vals))
+                res['alarms'].append(('create', Valarm.valarm2values(valarm)))
 
         if hasattr(ical, 'vtimezone'):
             if ical.vtimezone.tzid.value in pytz.common_timezones:
@@ -1035,7 +987,7 @@
             occurences_todel = [x.id for x in event.occurences]
         for vevent in vevents:
             event_id = None
-            vals = self.ical2values(event_id, ical, calendar_id, vevent=vevent)
+            vals = cls.ical2values(event_id, ical, calendar_id, vevent=vevent)
             if event:
                 for occurence in event.occurences:
                     if vals['recurrence'] == \
@@ -1056,31 +1008,17 @@
             res['occurences'].insert(0, ('delete', occurences_todel))
         return res
 
-    def event2ical(self, event):
+    def event2ical(self):
         '''
         Return an iCalendar instance of vobject for event
-
-        :param event: a BrowseRecord of calendar.event
-            or a calendar.event id
-        :param calendar: a BrowseRecord of calendar.calendar
-            or a calendar.calendar id
-        :return: an iCalendar instance of vobject
         '''
         pool = Pool()
-        user_obj = pool.get('res.user')
-        alarm_obj = pool.get('calendar.event.alarm')
-        attendee_obj = pool.get('calendar.event.attendee')
-        rdate_obj = pool.get('calendar.event.rdate')
-        exdate_obj = pool.get('calendar.event.exdate')
-        rrule_obj = pool.get('calendar.event.rrule')
-        exrule_obj = pool.get('calendar.event.exrule')
+        User = pool.get('res.user')
 
-        if isinstance(event, (int, long)):
-            event = self.browse(event)
-
-        user = user_obj.browse(Transaction().user)
-        if event.timezone:
-            tzevent = dateutil.tz.gettz(event.timezone)
+        user = User(Transaction().user)
+        if self.timezone:
+            tzevent = pytz.timezone(self.timezone)
+            tzevent = dateutil.tz.gettz(self.timezone)
         elif user.timezone:
             tzevent = dateutil.tz.gettz(user.timezone)
         else:
@@ -1088,248 +1026,222 @@
 
         ical = vobject.iCalendar()
         vevent = ical.add('vevent')
-        if event.vevent:
-            ical.vevent = vobject.readOne(str(event.vevent))
+        if self.vevent:
+            ical.vevent = vobject.readOne(str(self.vevent))
             vevent = ical.vevent
             ical.vevent.transformToNative()
-        if event.summary:
+        if self.summary:
             if not hasattr(vevent, 'summary'):
                 vevent.add('summary')
-            vevent.summary.value = event.summary
+            vevent.summary.value = self.summary
         elif hasattr(vevent, 'summary'):
             del vevent.summary
-        if event.description:
+        if self.description:
             if not hasattr(vevent, 'description'):
                 vevent.add('description')
-            vevent.description.value = event.description
+            vevent.description.value = self.description
         elif hasattr(vevent, 'description'):
             del vevent.description
         if not hasattr(vevent, 'dtstart'):
             vevent.add('dtstart')
-        if event.all_day:
-            vevent.dtstart.value = event.dtstart.date()
+        if self.all_day:
+            vevent.dtstart.value = self.dtstart.date()
         else:
-            vevent.dtstart.value = event.dtstart.replace(tzinfo=tzlocal)\
+            vevent.dtstart.value = self.dtstart.replace(tzinfo=tzlocal)\
                     .astimezone(tzevent)
-        if event.dtend:
+        if self.dtend:
             if not hasattr(vevent, 'dtend'):
                 vevent.add('dtend')
-            if event.all_day:
-                vevent.dtend.value = event.dtend.date()
+            if self.all_day:
+                vevent.dtend.value = self.dtend.date()
             else:
-                vevent.dtend.value = event.dtend.replace(tzinfo=tzlocal)\
+                vevent.dtend.value = self.dtend.replace(tzinfo=tzlocal)\
                         .astimezone(tzevent)
         elif hasattr(vevent, 'dtend'):
             del vevent.dtend
         if not hasattr(vevent, 'created'):
             vevent.add('created')
-        vevent.created.value = event.create_date.replace(tzinfo=tzlocal)
+        vevent.created.value = self.create_date.replace(tzinfo=tzlocal)
         if not hasattr(vevent, 'dtstamp'):
             vevent.add('dtstamp')
-        date = event.write_date or event.create_date
+        date = self.write_date or self.create_date
         vevent.dtstamp.value = date.replace(tzinfo=tzlocal)
         if not hasattr(vevent, 'last-modified'):
             vevent.add('last-modified')
         vevent.last_modified.value = date.replace(tzinfo=tzlocal)
-        if event.recurrence and event.parent:
+        if self.recurrence and self.parent:
             if not hasattr(vevent, 'recurrence-id'):
                 vevent.add('recurrence-id')
-            if event.all_day:
-                vevent.recurrence_id.value = event.recurrence.date()
+            if self.all_day:
+                vevent.recurrence_id.value = self.recurrence.date()
             else:
-                vevent.recurrence_id.value = event.recurrence\
+                vevent.recurrence_id.value = self.recurrence\
                         .replace(tzinfo=tzlocal).astimezone(tzevent)
         elif hasattr(vevent, 'recurrence-id'):
             del vevent.recurrence_id
-        if event.status:
+        if self.status:
             if not hasattr(vevent, 'status'):
                 vevent.add('status')
-            vevent.status.value = event.status.upper()
+            vevent.status.value = self.status.upper()
         elif hasattr(vevent, 'status'):
             del vevent.status
         if not hasattr(vevent, 'uid'):
             vevent.add('uid')
-        vevent.uid.value = event.uuid
+        vevent.uid.value = self.uuid
         if not hasattr(vevent, 'sequence'):
             vevent.add('sequence')
-        vevent.sequence.value = str(event.sequence) or '0'
-        if event.categories:
+        vevent.sequence.value = str(self.sequence) or '0'
+        if self.categories:
             if not hasattr(vevent, 'categories'):
                 vevent.add('categories')
-            vevent.categories.value = [x.name for x in event.categories]
+            vevent.categories.value = [x.name for x in self.categories]
         elif hasattr(vevent, 'categories'):
             del vevent.categories
         if not hasattr(vevent, 'class'):
             vevent.add('class')
-            getattr(vevent, 'class').value = event.classification.upper()
+            getattr(vevent, 'class').value = self.classification.upper()
         elif getattr(vevent, 'class').value.lower() in \
-                dict(self.classification.selection):
-            getattr(vevent, 'class').value = event.classification.upper()
-        if event.location:
+                dict(self.__class__.classification.selection):
+            getattr(vevent, 'class').value = self.classification.upper()
+        if self.location:
             if not hasattr(vevent, 'location'):
                 vevent.add('location')
-            vevent.location.value = event.location.name
+            vevent.location.value = self.location.name
         elif hasattr(vevent, 'location'):
             del vevent.location
 
         if not hasattr(vevent, 'transp'):
             vevent.add('transp')
-        vevent.transp.value = event.transp.upper()
+        vevent.transp.value = self.transp.upper()
 
-        if event.organizer:
+        if self.organizer:
             if not hasattr(vevent, 'organizer'):
                 vevent.add('organizer')
-            vevent.organizer.value = 'MAILTO:' + event.organizer
+            vevent.organizer.value = 'MAILTO:' + self.organizer
         elif hasattr(vevent, 'organizer'):
             del vevent.organizer
 
         vevent.attendee_list = []
-        for attendee in event.attendees:
-            vevent.attendee_list.append(attendee_obj.attendee2attendee(
-                attendee))
+        for attendee in self.attendees:
+            vevent.attendee_list.append(attendee.attendee2attendee())
 
-        if event.rdates:
+        if self.rdates:
             vevent.add('rdate')
             vevent.rdate.value = []
-            for rdate in event.rdates:
-                vevent.rdate.value.append(rdate_obj.date2date(rdate))
+            for rdate in self.rdates:
+                vevent.rdate.value.append(rdate.date2date())
 
-        if event.exdates:
+        if self.exdates:
             vevent.add('exdate')
             vevent.exdate.value = []
-            for exdate in event.exdates:
-                vevent.exdate.value.append(exdate_obj.date2date(exdate))
+            for exdate in self.exdates:
+                vevent.exdate.value.append(exdate.date2date())
 
-        if event.rrules:
-            for rrule in event.rrules:
-                vevent.add('rrule').value = rrule_obj.rule2rule(rrule)
+        if self.rrules:
+            for rrule in self.rrules:
+                vevent.add('rrule').value = rrule.rule2rule()
 
-        if event.exrules:
-            for exrule in event.exrules:
-                vevent.add('exrule').value = exrule_obj.rule2rule(exrule)
+        if self.exrules:
+            for exrule in self.exrules:
+                vevent.add('exrule').value = exrule.rule2rule()
 
         vevent.valarm_list = []
-        for alarm in event.alarms:
-            valarm = alarm_obj.alarm2valarm(alarm)
+        for alarm in self.alarms:
+            valarm = alarm.alarm2valarm()
             if valarm:
                 vevent.valarm_list.append(valarm)
 
-        for occurence in event.occurences:
-            oical = self.event2ical(occurence)
+        for occurence in self.occurences:
+            oical = occurence.event2ical()
             ical.vevent_list.append(oical.vevent)
         return ical
 
-Event()
-
 
 class EventCategory(ModelSQL):
     'Event - Category'
-    _description = __doc__
-    _name = 'calendar.event-calendar.category'
-
+    __name__ = 'calendar.event-calendar.category'
     event = fields.Many2One('calendar.event', 'Event', ondelete='CASCADE',
             required=True, select=True)
     category = fields.Many2One('calendar.category', 'Category',
             ondelete='CASCADE', required=True, select=True)
 
-EventCategory()
-
 
 class Alarm(ModelSQL):
     'Alarm'
-    _description = __doc__
-    _name = 'calendar.alarm'
-
+    __name__ = 'calendar.alarm'
     valarm = fields.Binary('valarm')
 
-    def valarm2values(self, valarm):
+    @classmethod
+    def valarm2values(cls, valarm):
         '''
         Convert a valarm object into values for create or write
+        '''
+        return {
+            'valarm': valarm.serialize(),
+            }
 
-        :param valarm: the valarm object
-        :return: a dictionary with values
-        '''
-        res = {}
-        res['valarm'] = valarm.serialize()
-        return res
-
-    def alarm2valarm(self, alarm):
+    def alarm2valarm(self):
         '''
         Return a valarm instance of vobject for alarm
-
-        :param alarm: a BrowseRecord of calendar.event.alarm
-        :return: a valarm instance of vobject
         '''
-        valarm = None
-        if alarm.valarm:
-            valarm = vobject.readOne(str(alarm.valarm))
-        return valarm
-
-Alarm()
+        if self.valarm:
+            return vobject.readOne(str(self.valarm))
 
 
 class EventAlarm(ModelSQL):
     'Alarm'
-    _description = __doc__
-    _name = 'calendar.event.alarm'
+    __name__ = 'calendar.event.alarm'
     _inherits = {'calendar.alarm': 'calendar_alarm'}
-
     calendar_alarm = fields.Many2One('calendar.alarm', 'Calendar Alarm',
             required=True, ondelete='CASCADE', select=True)
     event = fields.Many2One('calendar.event', 'Event', ondelete='CASCADE',
             required=True, select=True)
 
-    def create(self, values):
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def create(cls, values):
+        Event = Pool().get('calendar.event')
         if values.get('event'):
             # Update write_date of event
-            event_obj.write(values['event'], {})
-        return super(EventAlarm, self).create(values)
+            Event.write(values['event'], {})
+        return super(EventAlarm, cls).create(values)
 
-    def write(self, ids, values):
-        event_obj = Pool().get('calendar.event')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        event_ids = [x.event.id for x in self.browse(ids)]
+    @classmethod
+    def write(cls, event_alarms, values):
+        Event = Pool().get('calendar.event')
+        events = [x.event for x in event_alarms]
         if values.get('event'):
-            event_ids.append(values['event'])
-        if event_ids:
+            events.append(Event(values['event']))
+        if events:
             # Update write_date of event
-            event_obj.write(event_ids, {})
-        return super(EventAlarm, self).write(ids, values)
+            Event.write(events, {})
+        return super(EventAlarm, cls).write(event_alarms, values)
 
-    def delete(self, ids):
-        event_obj = Pool().get('calendar.event')
-        alarm_obj = Pool().get('calendar.alarm')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        event_alarms = self.browse(ids)
-        alarm_ids = [a.calendar_alarm.id for a in event_alarms]
-        event_ids = [x.event.id for x in event_alarms]
-        if event_ids:
+    @classmethod
+    def delete(cls, event_alarms):
+        pool = Pool()
+        Event = pool.get('calendar.event')
+        Alarm = pool.get('calendar.alarm')
+        alarms = [a.calendar_alarm for a in event_alarms]
+        events = [x.event for x in event_alarms]
+        if events:
             # Update write_date of event
-            event_obj.write(event_ids, {})
-        res = super(EventAlarm, self).delete(ids)
-        if alarm_ids:
-            alarm_obj.delete(alarm_ids)
-        return res
+            Event.write(events, {})
+        super(EventAlarm, cls).delete(event_alarms)
+        if alarms:
+            Alarm.delete(alarms)
 
-    def valarm2values(self, alarm):
-        alarm_obj = Pool().get('calendar.alarm')
-        return alarm_obj.valarm2values(alarm)
+    @classmethod
+    def valarm2values(cls, alarm):
+        Alarm = Pool().get('calendar.alarm')
+        return Alarm.valarm2values(alarm)
 
-    def alarm2valarm(self, alarm):
-        alarm_obj = Pool().get('calendar.alarm')
-        return alarm_obj.alarm2valarm(alarm)
-
-EventAlarm()
+    def alarm2valarm(self):
+        return self.calendar_alarm.alarm2valarm()
 
 
 class Attendee(ModelSQL, ModelView):
     'Attendee'
-    _description = __doc__
-    _name = 'calendar.attendee'
-
+    __name__ = 'calendar.attendee'
     email = fields.Char('Email', required=True, states={
         'readonly': Eval('id', 0) > 0,
         }, depends=['id'])
@@ -1343,20 +1255,19 @@
         ], 'Participation Status')
     attendee = fields.Binary('attendee')
 
-    def default_status(self):
+    @staticmethod
+    def default_status():
         return ''
 
-    def _attendee2update(self, attendee):
-        res = {}
-        res['status'] = attendee.status
-        return res
+    def _attendee2update(self):
+        return {
+            'status': self.status,
+            }
 
-    def attendee2values(self, attendee):
+    @classmethod
+    def attendee2values(cls, attendee):
         '''
         Convert a attendee object into values for create or write
-
-        :param attendee: the attendee object
-        :return: a dictionary with values
         '''
         res = {}
         if attendee.value.lower().startswith('mailto:'):
@@ -1365,60 +1276,53 @@
             res['email'] = attendee.value
         res['status'] = ''
         if hasattr(attendee, 'partstat_param'):
-            if attendee.partstat_param.lower() in dict(self.status.selection):
+            if attendee.partstat_param.lower() in dict(cls.status.selection):
                 res['status'] = attendee.partstat_param.lower()
         res['attendee'] = attendee.serialize()
         return res
 
-    def attendee2attendee(self, attendee):
+    def attendee2attendee(self):
         '''
         Return a attendee instance of vobject for attendee
-
-        :param attendee: a BrowseRecord of calendar.event.attendee
-        :return: a attendee instance of vobject
         '''
         res = None
-        if attendee.attendee:
+        if self.attendee:
             res = vobject.base.textLineToContentLine(
-                    str(attendee.attendee).replace('\r\n ', ''))
+                    str(self.attendee).replace('\r\n ', ''))
         else:
             res = vobject.base.ContentLine('ATTENDEE', [], '')
 
-        if attendee.status:
+        if self.status:
             if hasattr(res, 'partstat_param'):
                 if res.partstat_param.lower() in dict(self.status.selection):
-                    res.partstat_param = attendee.status.upper()
+                    res.partstat_param = self.status.upper()
             else:
-                res.partstat_param = attendee.status.upper()
+                res.partstat_param = self.status.upper()
         elif hasattr(res, 'partstat_param'):
             if res.partstat_param.lower() in dict(self.status.selection):
                 del res.partstat_param
 
-        res.value = 'MAILTO:' + attendee.email
+        res.value = 'MAILTO:' + self.email
         return res
 
-Attendee()
-
 
 class EventAttendee(ModelSQL, ModelView):
     'Attendee'
-    _description = __doc__
-    _name = 'calendar.event.attendee'
+    __name__ = 'calendar.event.attendee'
     _inherits = {'calendar.attendee': 'calendar_attendee'}
-
     calendar_attendee = fields.Many2One('calendar.attendee',
         'Calendar Attendee', required=True, ondelete='CASCADE', select=True)
     event = fields.Many2One('calendar.event', 'Event', ondelete='CASCADE',
         required=True, select=True)
 
-    def create(self, values):
-        event_obj = Pool().get('calendar.event')
+    @staticmethod
+    def create(cls, values):
+        Event = Pool().get('calendar.event')
         if values.get('event'):
             # Update write_date of event
-            event_obj.write(values['event'], {})
-        res = super(EventAttendee, self).create(values)
-        attendee = self.browse(res)
-        event = attendee.event
+            Event.write([Event(values['event'])], {})
+        event_attendee = super(EventAttendee, cls).create(values)
+        event = event_attendee.event
         if (event.calendar.owner
                 and (event.organizer == event.calendar.owner.email
                     or (event.parent
@@ -1432,36 +1336,72 @@
                         if x.email != event.parent.organizer]
             if attendee_emails:
                 with Transaction().set_user(0):
-                    event_ids = event_obj.search([
-                        ('uuid', '=', event.uuid),
-                        ('calendar.owner.email', 'in', attendee_emails),
-                        ('id', '!=', event.id),
-                        ('recurrence', '=', event.recurrence),
-                        ])
-                    for event_id in event_ids:
-                        self.copy(res, default={
-                            'event': event_id,
-                            })
-        return res
+                    events = Event.search([
+                            ('uuid', '=', event.uuid),
+                            ('calendar.owner.email', 'in', attendee_emails),
+                            ('id', '!=', event.id),
+                            ('recurrence', '=', event.recurrence),
+                            ])
+                    for event in events:
+                        cls.copy([event_attendee], default={
+                                'event': event.id,
+                                })
+        return event_attendee
 
-    def write(self, ids, values):
-        event_obj = Pool().get('calendar.event')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        event_ids = [x.event.id for x in self.browse(ids)]
+    @classmethod
+    def write(cls, event_attendees, values):
+        Event = Pool().get('calendar.event')
+        events = [x.event.id for x in event_attendees]
         if values.get('event'):
-            event_ids.append(values['event'])
-        if event_ids:
+            events.append(Event(values['event']))
+        if events:
             # Update write_date of event
-            event_obj.write(event_ids, {})
+            Event.write(events, {})
 
         if 'email' in values:
             values = values.copy()
             del values['email']
 
-        res = super(EventAttendee, self).write(ids, values)
-        attendees = self.browse(ids)
-        for attendee in attendees:
+        super(EventAttendee, cls).write(event_attendees, values)
+        for event_attendee in event_attendees:
+            event = event_attendee.event
+            if event.calendar.owner \
+                    and (event.organizer == event.calendar.owner.email \
+                    or (event.parent \
+                    and event.parent.organizer == event.calendar.owner.email)):
+                if event.organizer == event.calendar.owner.email:
+                    attendee_emails = [x.email for x in event.attendees
+                            if x.email != event.organizer]
+                else:
+                    attendee_emails = [x.email for x in event.parent.attendees
+                            if x.email != event.parent.organizer]
+                if attendee_emails:
+                    with Transaction().set_user(0):
+                        other_attendees = cls.search([
+                                ('event.uuid', '=', event.uuid),
+                                ('event.calendar.owner.email', 'in',
+                                    attendee_emails),
+                                ('id', '!=', event_attendee.id),
+                                ('event.recurrence', '=',
+                                    event.recurrence),
+                                ('email', '=', event_attendee.email),
+                                ])
+                        cls.write(other_attendees,
+                            event_attendee._attendee2update())
+
+    @classmethod
+    def delete(cls, event_attendees):
+        pool = Pool()
+        Event = pool.get('calendar.event')
+        Attendee = pool.get('calendar.attendee')
+
+        calendar_attendees = [a.calendar_attendee for a in event_attendees]
+        events = [x.event for x in event_attendees]
+        if events:
+            # Update write_date of event
+            Event.write(events, {})
+
+        for attendee in event_attendees:
             event = attendee.event
             if event.calendar.owner \
                     and (event.organizer == event.calendar.owner.email \
@@ -1475,57 +1415,16 @@
                             if x.email != event.parent.organizer]
                 if attendee_emails:
                     with Transaction().set_user(0):
-                        attendee_ids = self.search([
-                            ('event.uuid', '=', event.uuid),
-                            ('event.calendar.owner.email', 'in',
-                                attendee_emails),
-                            ('id', '!=', attendee.id),
-                            ('event.recurrence', '=',
-                                event.recurrence),
-                            ('email', '=', attendee.email),
-                            ])
-                        self.write(attendee_ids, self._attendee2update(
-                            attendee))
-        return res
-
-    def delete(self, ids):
-        event_obj = Pool().get('calendar.event')
-        attendee_obj = Pool().get('calendar.attendee')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        event_attendees = self.browse(ids)
-        calendar_attendee_ids = [a.calendar_attendee.id \
-                for a in event_attendees]
-        event_ids = [x.event.id for x in event_attendees]
-        if event_ids:
-            # Update write_date of event
-            event_obj.write(event_ids, {})
-
-        for attendee in self.browse(ids):
-            event = attendee.event
-            if event.calendar.owner \
-                    and (event.organizer == event.calendar.owner.email \
-                    or (event.parent \
-                    and event.parent.organizer == event.calendar.owner.email)):
-                if event.organizer == event.calendar.owner.email:
-                    attendee_emails = [x.email for x in event.attendees
-                            if x.email != event.organizer]
-                else:
-                    attendee_emails = [x.email for x in event.parent.attendees
-                            if x.email != event.parent.organizer]
-                if attendee_emails:
-                    with Transaction().set_user(0):
-                        attendee_ids = self.search([
-                            ('event.uuid', '=', event.uuid),
-                            ('event.calendar.owner.email', 'in',
-                                attendee_emails),
-                            ('id', '!=', attendee.id),
-                            ('event.recurrence', '=',
-                                event.recurrence),
-                            ('email', '=', attendee.email),
-                            ])
-                        self.delete(attendee_ids)
+                        attendees = cls.search([
+                                ('event.uuid', '=', event.uuid),
+                                ('event.calendar.owner.email', 'in',
+                                    attendee_emails),
+                                ('id', '!=', attendee.id),
+                                ('event.recurrence', '=',
+                                    event.recurrence),
+                                ('email', '=', attendee.email),
+                                ])
+                        cls.delete(attendees)
             elif event.calendar.owner \
                     and ((event.organizer \
                     or (event.parent and event.parent.organizer)) \
@@ -1535,89 +1434,76 @@
                 else:
                     organizer = event.parent.organizer
                 with Transaction().set_user(0):
-                    attendee_ids = self.search([
-                        ('event.uuid', '=', event.uuid),
-                        ('event.calendar.owner.email', '=', organizer),
-                        ('id', '!=', attendee.id),
-                        ('event.recurrence', '=', event.recurrence),
-                        ('email', '=', attendee.email),
-                        ])
-                    if attendee_ids:
-                        self.write(attendee_ids, {
-                            'status': 'declined',
-                            })
-        res = super(EventAttendee, self).delete(ids)
-        if calendar_attendee_ids:
-            attendee_obj.delete(calendar_attendee_ids)
-        return res
+                    attendees = cls.search([
+                            ('event.uuid', '=', event.uuid),
+                            ('event.calendar.owner.email', '=', organizer),
+                            ('id', '!=', attendee.id),
+                            ('event.recurrence', '=', event.recurrence),
+                            ('email', '=', attendee.email),
+                            ])
+                    if attendees:
+                        cls.write(attendees, {
+                                'status': 'declined',
+                                })
+        super(EventAttendee, cls).delete(event_attendees)
+        if calendar_attendees:
+            Attendee.delete(calendar_attendees)
 
-    def copy(self, ids, default=None):
-        attendee_obj = Pool().get('calendar.attendee')
+    @classmethod
+    def copy(cls, event_attendees, default=None):
+        Attendee = Pool().get('calendar.attendee')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for attendee in self.browse(ids):
-            default['calendar_attendee'] = attendee_obj.copy(
-                    attendee.calendar_attendee.id)
-            new_id = super(EventAttendee, self).copy(attendee.id,
-                    default=default)
-            new_ids.append(new_id)
-        if int_id:
-            return new_ids[0]
-        return new_ids
+        new_event_attendees = []
+        for event_attendee in event_attendees:
+            default['calendar_attendee'], = Attendee.copy(
+                    [event_attendee.calendar_attendee])
+            new_event_attendees.extend(super(EventAttendee, cls).copy(
+                    [event_attendee], default=default))
+        return new_event_attendees
 
-    def _attendee2update(self, attendee):
-        attendee_obj = Pool().get('calendar.attendee')
-        return attendee_obj._attendee2update(attendee)
+    def _attendee2update(self):
+        return self.calendar_attendee._attendee2update()
 
-    def attendee2values(self, attendee):
-        attendee_obj = Pool().get('calendar.attendee')
-        return attendee_obj.attendee2values(attendee)
+    @staticmethod
+    def attendee2values(attendee):
+        Attendee = Pool().get('calendar.attendee')
+        return Attendee.attendee2values(attendee)
 
-    def attendee2attendee(self, attendee):
-        attendee_obj = Pool().get('calendar.attendee')
-        return attendee_obj.attendee2attendee(attendee)
-
-EventAttendee()
+    def attendee2attendee(self):
+        return self.calendar_attendee.attendee2attendee()
 
 
 class Date(ModelSQL, ModelView):
     'Calendar Date'
-    _description = __doc__
-    _name = 'calendar.date'
+    __name__ = 'calendar.date'
     _rec_name = 'datetime'
-
     date = fields.Boolean('Is Date', help='Ignore time of field "Date", ' \
             'but handle as date only.')
     datetime = fields.DateTime('Date', required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.4: calendar.rdate renamed to calendar.date
         old_table = 'calendar_rdate'
         if TableHandler.table_exist(cursor, old_table):
-            TableHandler.table_rename(cursor, old_table, self._table)
+            TableHandler.table_rename(cursor, old_table, cls._table)
 
-        return super(Date, self).init(module_name)
+        super(Date, cls).__register__(module_name)
 
-    def _date2update(self, date):
-        res = {}
-        res['date'] = date.date
-        res['datetime'] = date.datetime
-        return res
+    def _date2update(self):
+        return {
+            'date': self.date,
+            'datetime': self.datetime,
+            }
 
-    def date2values(self, date):
+    @staticmethod
+    def date2values(date):
         '''
         Convert a date object into values for create or write
-
-        :param date: the date object
-        :return: a dictionary with values
         '''
         res = {}
         if not isinstance(date, datetime.datetime):
@@ -1632,108 +1518,91 @@
                 res['datetime'] = date
         return res
 
-    def date2date(self, date):
+    def date2date(self):
         '''
         Return a datetime for date
-
-        :param date: a BrowseRecord of calendar.date or
-            calendar.exdate
-        :return: a datetime
         '''
-        if date.date:
-            res = date.datetime.date()
+        if self.date:
+            return self.date.datetime.date()
         else:
             # Convert to UTC as sunbird doesn't handle tzid
-            res = date.datetime.replace(tzinfo=tzlocal).astimezone(tzutc)
-        return res
-
-Date()
+            return self.datetime.replace(tzinfo=tzlocal).astimezone(tzutc)
 
 
 class EventRDate(ModelSQL, ModelView):
     'Recurrence Date'
-    _description = __doc__
-    _name = 'calendar.event.rdate'
+    __name__ = 'calendar.event.rdate'
     _inherits = {'calendar.date': 'calendar_date'}
     _rec_name = 'datetime'
-
     calendar_date = fields.Many2One('calendar.date', 'Calendar Date',
             required=True, ondelete='CASCADE', select=True)
     event = fields.Many2One('calendar.event', 'Event', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.4: calendar_rdate renamed to calendar_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         old_column = 'calendar_rdate'
         if table.column_exist(old_column):
             table.column_rename(old_column, 'calendar_date')
 
-        return super(EventRDate, self).init(module_name)
+        super(EventRDate, cls).__register__(module_name)
 
-    def create(self, values):
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def create(cls, values):
+        Event = Pool().get('calendar.event')
         if values.get('event'):
             # Update write_date of event
-            event_obj.write(values['event'], {})
-        return super(EventRDate, self).create(values)
+            Event.write([Event(values['event'])], {})
+        return super(EventRDate, cls).create(values)
 
-    def write(self, ids, values):
-        event_obj = Pool().get('calendar.event')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        event_ids = [x.event.id for x in self.browse(ids)]
+    @classmethod
+    def write(cls, event_rdates, values):
+        Event = Pool().get('calendar.event')
+        events = [x.event for x in event_rdates]
         if values.get('event'):
-            event_ids.append(values['event'])
-        if event_ids:
+            events.append(Event(values['event']))
+        if events:
             # Update write_date of event
-            event_obj.write(event_ids, {})
-        return super(EventRDate, self).write(ids, values)
+            Event.write(events, {})
+        super(EventRDate, cls).write(event_rdates, values)
 
-    def delete(self, ids):
-        event_obj = Pool().get('calendar.event')
-        rdate_obj = Pool().get('calendar.date')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        event_rdates = self.browse(ids)
-        rdate_ids = [a.calendar_date.id for a in event_rdates]
-        event_ids = [x.event.id for x in event_rdates]
-        if event_ids:
+    @classmethod
+    def delete(cls, event_rdates):
+        pool = Pool()
+        Event = pool.get('calendar.event')
+        Date = pool.get('calendar.date')
+        dates = [a.calendar_date for a in event_rdates]
+        events = [x.event for x in event_rdates]
+        if events:
             # Update write_date of event
-            event_obj.write(event_ids, {})
-        res = super(EventRDate, self).delete(ids)
-        if rdate_ids:
-            rdate_obj.delete(rdate_ids)
-        return res
+            Event.write(events, {})
+        super(EventRDate, cls).delete(event_rdates)
+        if dates:
+            Date.delete(dates)
 
-    def _date2update(self, date):
-        date_obj = Pool().get('calendar.date')
-        return date_obj._date2update(date)
+    def _date2update(self):
+        return self.calendar_date._date2update()
 
-    def date2values(self, date):
-        date_obj = Pool().get('calendar.date')
-        return date_obj.date2values(date)
+    @classmethod
+    def date2values(cls, date):
+        Date = Pool().get('calendar.date')
+        return Date.date2values(date)
 
-    def date2date(self, date):
-        date_obj = Pool().get('calendar.date')
-        return date_obj.date2date(date)
-
-EventRDate()
+    def date2date(self):
+        return self.calendar_date.date2date()
 
 
 class EventExDate(EventRDate):
     'Exception Date'
-    _description = __doc__
-    _name = 'calendar.event.exdate'
-
-EventExDate()
+    __name__ = 'calendar.event.exdate'
 
 
 class RRule(ModelSQL, ModelView):
     'Recurrence Rule'
-    _description = __doc__
-    _name = 'calendar.rrule'
+    __name__ = 'calendar.rrule'
     _rec_name = 'freq'
 
     freq = fields.Selection([
@@ -1769,14 +1638,15 @@
         ('sa', 'Saturday'),
         ], 'Week Day', sort=False)
 
-    def __init__(self):
-        super(RRule, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(RRule, cls).__setup__()
+        cls._sql_constraints += [
             ('until_count_only_one',
                 'CHECK(until IS NULL OR count IS NULL OR count = 0)',
                 'Only one of "until" and "count" can be set!'),
-        ]
-        self._constraints += [
+            ]
+        cls._constraints += [
             ('check_bysecond', 'invalid_bysecond'),
             ('check_byminute', 'invalid_byminute'),
             ('check_byhour', 'invalid_byhour'),
@@ -1786,31 +1656,30 @@
             ('check_byweekno', 'invalid_byweekno'),
             ('check_bymonth', 'invalid_bymonth'),
             ('check_bysetpos', 'invalid_bysetpos'),
-        ]
-        self._error_messages.update({
-            'invalid_bysecond': 'Invalid "By Second"',
-            'invalid_byminute': 'Invalid "By Minute"',
-            'invalid_byhour': 'Invalid "By Hour"',
-            'invalid_byday': 'Invalid "By Day"',
-            'invalid_bymonthday': 'Invalid "By Month Day"',
-            'invalid_byyearday': 'Invalid "By Year Day"',
-            'invalid_byweekno': 'Invalid "By Week Number"',
-            'invalid_bymonth': 'Invalid "By Month"',
-            'invalid_bysetpos': 'Invalid "By Position"',
-        })
+            ]
+        cls._error_messages.update({
+                'invalid_bysecond': 'Invalid "By Second"',
+                'invalid_byminute': 'Invalid "By Minute"',
+                'invalid_byhour': 'Invalid "By Hour"',
+                'invalid_byday': 'Invalid "By Day"',
+                'invalid_bymonthday': 'Invalid "By Month Day"',
+                'invalid_byyearday': 'Invalid "By Year Day"',
+                'invalid_byweekno': 'Invalid "By Week Number"',
+                'invalid_bymonth': 'Invalid "By Month"',
+                'invalid_bysetpos': 'Invalid "By Position"',
+                })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migrate from 1.4: unit_count replaced by until_count_only_one
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.drop_constraint('until_count')
-        return super(RRule, self).init(module_name)
+        return super(RRule, cls).__register__(module_name)
 
-    def check_bysecond(self, ids):
-        for rule in self.browse(ids):
-            if not rule.bysecond:
-                continue
-            for second in rule.bysecond.split(','):
+    def check_bysecond(self):
+        if self.bysecond:
+            for second in self.bysecond.split(','):
                 try:
                     second = int(second)
                 except Exception:
@@ -1819,11 +1688,9 @@
                     return False
         return True
 
-    def check_byminute(self, ids):
-        for rule in self.browse(ids):
-            if not rule.byminute:
-                continue
-            for minute in rule.byminute.split(','):
+    def check_byminute(self):
+        if self.byminute:
+            for minute in self.byminute.split(','):
                 try:
                     minute = int(minute)
                 except Exception:
@@ -1832,11 +1699,9 @@
                     return False
         return True
 
-    def check_byhour(self, ids):
-        for rule in self.browse(ids):
-            if not rule.byhour:
-                continue
-            for hour in rule.byhour.split(','):
+    def check_byhour(self):
+        if self.byhour:
+            for hour in self.byhour.split(','):
                 try:
                     hour = int(hour)
                 except Exception:
@@ -1845,11 +1710,9 @@
                     return False
         return True
 
-    def check_byday(self, ids):
-        for rule in self.browse(ids):
-            if not rule.byday:
-                continue
-            for weekdaynum in rule.byday.split(','):
+    def check_byday(self):
+        if self.byday:
+            for weekdaynum in self.byday.split(','):
                 weekday = weekdaynum[-2:]
                 if weekday not in ('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'):
                     return False
@@ -1864,11 +1727,9 @@
                     return False
         return True
 
-    def check_bymonthday(self, ids):
-        for rule in self.browse(ids):
-            if not rule.bymonthday:
-                continue
-            for monthdaynum in rule.bymonthday.split(','):
+    def check_bymonthday(self):
+        if self.bymonthday:
+            for monthdaynum in self.bymonthday.split(','):
                 try:
                     monthdaynum = int(monthdaynum)
                 except Exception:
@@ -1877,11 +1738,9 @@
                     return False
         return True
 
-    def check_byyearday(self, ids):
-        for rule in self.browse(ids):
-            if not rule.byyearday:
-                continue
-            for yeardaynum in rule.byyearday.split(','):
+    def check_byyearday(self):
+        if self.byyearday:
+            for yeardaynum in self.byyearday.split(','):
                 try:
                     yeardaynum = int(yeardaynum)
                 except Exception:
@@ -1890,11 +1749,9 @@
                     return False
         return True
 
-    def check_byweekno(self, ids):
-        for rule in self.browse(ids):
-            if not rule.byweekno:
-                continue
-            for weeknum in rule.byweekno.split(','):
+    def check_byweekno(self):
+        if self.byweekno:
+            for weeknum in self.byweekno.split(','):
                 try:
                     weeknum = int(weeknum)
                 except Exception:
@@ -1903,11 +1760,9 @@
                     return False
         return True
 
-    def check_bymonth(self, ids):
-        for rule in self.browse(ids):
-            if not rule.bymonth:
-                continue
-            for monthnum in rule.bymonth.split(','):
+    def check_bymonth(self):
+        if self.bymonth:
+            for monthnum in self.bymonth.split(','):
                 try:
                     monthnum = int(monthnum)
                 except Exception:
@@ -1916,11 +1771,9 @@
                     return False
         return True
 
-    def check_bysetpos(self, ids):
-        for rule in self.browse(ids):
-            if not rule.bysetpos:
-                continue
-            for setposday in rule.bysetpos.split(','):
+    def check_bysetpos(self):
+        if self.bysetpos:
+            for setposday in self.bysetpos.split(','):
                 try:
                     setposday = int(setposday)
                 except Exception:
@@ -1929,20 +1782,18 @@
                     return False
         return True
 
-    def _rule2update(self, rule):
+    def _rule2update(self):
         res = {}
         for field in ('freq', 'until_date', 'until', 'count', 'interval',
                 'bysecond', 'byminute', 'byhour', 'byday', 'bymonthday',
                 'byyearday', 'byweekno', 'bymonth', 'bysetpos', 'wkst'):
-            res[field] = rule[field]
+            res[field] = getattr(self, field)
         return res
 
-    def rule2values(self, rule):
+    @classmethod
+    def rule2values(cls, rule):
         '''
         Convert a rule object into values for create or write
-
-        :param rule: teh rule object
-        :return: a dictionary with values
         '''
         res = {}
         for attr in str(rule.value).replace('\\', '').split(';'):
@@ -1969,42 +1820,35 @@
                 res[field] = value
         return res
 
-    def rule2rule(self, rule):
+    def rule2rule(self):
         '''
         Return a rule string for rule
-
-        :param rule: a BrowseRecord of calendar.rrule or
-            calendar.exrule
-        :return: a string
         '''
-        res = 'FREQ=' + rule.freq.upper()
-        if rule.until:
+        res = 'FREQ=' + self.freq.upper()
+        if self.until:
             res += ';UNTIL='
-            if rule.until_date:
-                res += vobject.icalendar.dateToString(rule.until.date())
+            if self.until_date:
+                res += vobject.icalendar.dateToString(self.until.date())
             else:
-                res += vobject.icalendar.dateTimeToString(rule.until\
+                res += vobject.icalendar.dateTimeToString(self.until\
                         .replace(tzinfo=tzlocal).astimezone(tzutc),
                         convertToUTC=True)
-        elif rule.count:
-            res += ';COUNT=' + str(rule.count)
+        elif self.count:
+            res += ';COUNT=' + str(self.count)
         for field in ('freq', 'wkst'):
-            if rule[field]:
-                res += ';' + field.upper() + '=' + rule[field].upper()
+            if getattr(self, field):
+                res += ';' + field.upper() + '=' + getattr(self, field).upper()
         for field in ('interval', 'bysecond', 'byminute', 'byhour',
                 'byday', 'bymonthday', 'byyearday', 'byweekno',
                 'bymonth', 'bysetpos'):
-            if rule[field]:
-                res += ';' + field.upper() + '=' + str(rule[field])
+            if getattr(self, field):
+                res += ';' + field.upper() + '=' + str(getattr(self, field))
         return res
 
-RRule()
-
 
 class EventRRule(ModelSQL, ModelView):
     'Recurrence Rule'
-    _description = __doc__
-    _name = 'calendar.event.rrule'
+    __name__ = 'calendar.event.rrule'
     _inherits = {'calendar.rrule': 'calendar_rrule'}
     _rec_name = 'freq'
 
@@ -2013,59 +1857,52 @@
     event = fields.Many2One('calendar.event', 'Event', ondelete='CASCADE',
             select=True, required=True)
 
-    def create(self, values):
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def create(cls, values):
+        Event = Pool().get('calendar.event')
         if values.get('event'):
             # Update write_date of event
-            event_obj.write(values['event'], {})
-        return super(EventRRule, self).create(values)
+            Event.write([Event(values['event'])], {})
+        return super(EventRRule, cls).create(values)
 
-    def write(self, ids, values):
-        event_obj = Pool().get('calendar.event')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        event_ids = [x.event.id for x in self.browse(ids)]
+    @classmethod
+    def write(cls, event_rrules, values):
+        Event = Pool().get('calendar.event')
+        events = [x.event for x in event_rrules]
         if values.get('event'):
-            event_ids.append(values['event'])
-        if event_ids:
+            events.append(Event(values['event']))
+        if events:
             # Update write_date of event
-            event_obj.write(event_ids, {})
-        return super(EventRRule, self).write(ids, values)
+            Event.write(events, {})
+        super(EventRRule, cls).write(event_rrules, values)
 
-    def delete(self, ids):
-        event_obj = Pool().get('calendar.event')
-        rrule_obj = Pool().get('calendar.rrule')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        event_rrules = self.browse(ids)
-        rrule_ids = [a.calendar_rrule.id for a in event_rrules]
-        event_ids = [x.event.id for x in event_rrules]
-        if event_ids:
+    @classmethod
+    def delete(cls, event_rrules):
+        pool = Pool()
+        Event = pool.get('calendar.event')
+        Rrule = pool.get('calendar.rrule')
+        rrules = [a.calendar_rrule for a in event_rrules]
+        events = [x.event for x in event_rrules]
+        if events:
             # Update write_date of event
-            event_obj.write(event_ids, {})
-        res = super(EventRRule, self).delete(ids)
-        if rrule_ids:
-            rrule_obj.delete(rrule_ids)
-        return res
+            Event.write(events, {})
+        super(EventRRule, cls).delete(event_rrules)
+        if rrules:
+            Rrule.delete(rrules)
 
-    def _rule2update(self, rule):
-        rule_obj = Pool().get('calendar.rrule')
-        return rule_obj._rule2update(rule)
+    def _rule2update(self):
+        return self.calendar_rrule._rule2update()
 
+    @classmethod
     def rule2values(self, rule):
-        rule_obj = Pool().get('calendar.rrule')
-        return rule_obj.rule2values(rule)
+        Rule = Pool().get('calendar.rrule')
+        return Rule.rule2values(rule)
 
-    def rule2rule(self, rule):
-        rule_obj = Pool().get('calendar.rrule')
-        return rule_obj.rule2rule(rule)
-
-EventRRule()
+    def rule2rule(self):
+        return self.calendar_rrule.rule2rule()
 
 
 class EventExRule(EventRRule):
     'Exception Rule'
-    _description = __doc__
-    _name = 'calendar.event.exrule'
-
-EventExRule()
+    __name__ = 'calendar.event.exrule'
+    _table = 'calendar_event_exrule'  # Needed to override EventRRule._table
diff -r ec894ddf18f6 trytond/modules/calendar/res.py
--- a/trytond/modules/calendar/res.py	Wed Jul 25 20:55:21 2012 +0200
+++ b/trytond/modules/calendar/res.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,27 +1,26 @@
 #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 copy
-from trytond.model import ModelView, ModelSQL, fields
+from trytond.model import fields
+from trytond.pool import PoolMeta
 from trytond.pyson import Bool, Eval
 
+__all__ = ['User']
+__metaclass__ = PoolMeta
 
-class User(ModelSQL, ModelView):
-    _name = 'res.user'
+
+class User:
+    __name__ = 'res.user'
 
     calendars = fields.One2Many('calendar.calendar', 'owner', 'Calendars')
 
-    def __init__(self):
-        super(User, self).__init__()
-        self.email = copy.copy(self.email)
-        self.email.states = copy.copy(self.email.states)
-        self.email.depends = copy.copy(self.email.depends)
+    @classmethod
+    def __setup__(cls):
+        super(User, cls).__setup__()
         required = Bool(Eval('calendars'))
-        if not self.email.states.get('required'):
-            self.email.states['required'] = required
+        if not cls.email.states.get('required'):
+            cls.email.states['required'] = required
         else:
-            self.email.states['required'] = (
-                self.email.states['required'] | required)
-        if 'calendars' not in self.email.depends:
-            self.email.depends.append('calendars')
-
-User()
+            cls.email.states['required'] = (
+                cls.email.states['required'] | required)
+        if 'calendars' not in cls.email.depends:
+            cls.email.depends.append('calendars')
diff -r ec894ddf18f6 trytond/modules/calendar/webdav.py
--- a/trytond/modules/calendar/webdav.py	Wed Jul 25 20:55:21 2012 +0200
+++ b/trytond/modules/calendar/webdav.py	Mon Sep 24 20:36:40 2012 +0200
@@ -3,11 +3,13 @@
 import vobject
 import urllib
 from pywebdav.lib.errors import DAV_NotFound, DAV_Forbidden
-from trytond.model import ModelView, ModelSQL
 from trytond.tools import reduce_ids
 from trytond.cache import Cache
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
+
+__all__ = ['Collection']
+__metaclass__ = PoolMeta
 
 CALDAV_NS = 'urn:ietf:params:xml:ns:caldav'
 
@@ -45,19 +47,16 @@
             ]]
 
 
-class Collection(ModelSQL, ModelView):
+class Collection:
+    __name__ = "webdav.collection"
+    _event_cache = Cache('webdav_collection.event')
 
-    _name = "webdav.collection"
-
-    def calendar(self, uri, ics=False):
+    @staticmethod
+    def calendar(uri, ics=False):
         '''
         Return the calendar id in the uri
-
-        :param uri: the uri
-        :return: calendar id
-            or None if there is no calendar
         '''
-        calendar_obj = Pool().get('calendar.calendar')
+        Calendar = Pool().get('calendar.calendar')
 
         if uri and uri.startswith('Calendars/'):
             calendar, uri = (uri[10:].split('/', 1) + [None])[0:2]
@@ -66,40 +65,41 @@
                     calendar = calendar[:-4]
                 else:
                     return None
-            return calendar_obj.get_name(calendar)
+            return Calendar.get_name(calendar)
 
-    @Cache('webdav_collection.event')
-    def event(self, uri, calendar_id=False):
+    @classmethod
+    def event(cls, uri, calendar_id=False):
         '''
         Return the event id in the uri
+        '''
+        Event = Pool().get('calendar.event')
 
-        :param uri: the uri
-        :param calendar_id: the calendar id
-        :return: event id
-            or None if there is no event
-        '''
-        event_obj = Pool().get('calendar.event')
-
+        key = (uri, calendar_id)
+        event_id = cls._event_cache.get(key, default=-1)
+        if event_id != -1:
+            return event_id
+        else:
+            event_id = None
         if uri and uri.startswith('Calendars/'):
             calendar, event_uri = (uri[10:].split('/', 1) + [None])[0:2]
             if not calendar_id:
-                calendar_id = self.calendar(uri)
+                calendar_id = cls.calendar(uri)
                 if not calendar_id:
                     return None
-            event_ids = event_obj.search([
-                ('calendar', '=', calendar_id),
-                ('uuid', '=', event_uri[:-4]),
-                ('parent', '=', None),
-                ], limit=1)
-            if event_ids:
-                return event_ids[0]
+            events = Event.search([
+                    ('calendar', '=', calendar_id),
+                    ('uuid', '=', event_uri[:-4]),
+                    ('parent', '=', None),
+                    ], limit=1)
+            if events:
+                event_id = events[0].id
+        cls._event_cache.set(key, event_id)
+        return event_id
 
-    def _caldav_filter_domain_calendar(self, filter):
+    @staticmethod
+    def _caldav_filter_domain_calendar(filter):
         '''
         Return a domain for caldav filter on calendar
-
-        :param filter: the DOM Element of filter
-        :return: a list for domain
         '''
         if not filter:
             return []
@@ -107,12 +107,10 @@
             return [('id', '=', 0)]
         return [('id', '=', 0)]
 
-    def _caldav_filter_domain_event(self, filter):
+    @classmethod
+    def _caldav_filter_domain_event(cls, filter):
         '''
         Return a domain for caldav filter on event
-
-        :param filter: the DOM Element of filter
-        :return: a list for domain
         '''
         res = []
         if not filter:
@@ -174,107 +172,111 @@
                         continue
                     if uri:
                         uri = urllib.unquote_plus(uri)
-                    event_id = self.event(uri)
+                    event_id = cls.event(uri)
                     if event_id:
                         ids.append(event_id)
             return [('id', 'in', ids)]
         return res
 
-    def get_childs(self, uri, filter=None, cache=None):
-        calendar_obj = Pool().get('calendar.calendar')
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def get_childs(cls, uri, filter=None, cache=None):
+        pool = Pool()
+        Calendar = pool.get('calendar.calendar')
+        Event = pool.get('calendar.event')
 
         if uri in ('Calendars', 'Calendars/'):
-            domain = self._caldav_filter_domain_calendar(filter)
+            domain = cls._caldav_filter_domain_calendar(filter)
             domain = [['OR',
                     ('owner', '=', Transaction().user),
                     ('read_users', '=', Transaction().user),
                     ],
                 domain]
-            calendar_ids = calendar_obj.search(domain)
-            calendars = calendar_obj.browse(calendar_ids)
+            calendars = Calendar.search(domain)
             if cache is not None:
                 cache.setdefault('_calendar', {})
-                cache['_calendar'].setdefault(calendar_obj._name, {})
-                for calendar_id in calendar_ids:
-                    cache['_calendar'][calendar_obj._name][calendar_id] = {}
+                cache['_calendar'].setdefault(Calendar.__name__, {})
+                for calendar in calendars:
+                    cache['_calendar'][Calendar.__name__][calendar.id] = {}
             return [x.name for x in calendars] + \
                     [x.name + '.ics' for x in calendars]
         if uri and uri.startswith('Calendars/'):
-            calendar_id = self.calendar(uri)
+            calendar_id = cls.calendar(uri)
             if  calendar_id and not (uri[10:].split('/', 1) + [None])[1]:
-                domain = self._caldav_filter_domain_event(filter)
-                event_ids = event_obj.search([
-                    ('calendar', '=', calendar_id),
-                    domain,
-                    ])
-                events = event_obj.browse(event_ids)
+                domain = cls._caldav_filter_domain_event(filter)
+                events = Event.search([
+                        ('calendar', '=', calendar_id),
+                        domain,
+                        ])
                 if cache is not None:
                     cache.setdefault('_calendar', {})
-                    cache['_calendar'].setdefault(event_obj._name, {})
-                    for event_id in event_ids:
-                        cache['_calendar'][event_obj._name][event_id] = {}
+                    cache['_calendar'].setdefault(Event.__name__, {})
+                    for event in events:
+                        cache['_calendar'][Event.__name__][event.id] = {}
                 return [x.uuid + '.ics' for x in events]
             return []
-        res = super(Collection, self).get_childs(uri, filter=filter,
-                cache=cache)
+        childs = super(Collection, cls).get_childs(uri, filter=filter,
+            cache=cache)
         if not uri and not filter:
-            res.append('Calendars')
+            childs.append('Calendars')
         elif not uri and filter:
             if filter.localName == 'principal-property-search':
-                res.append('Calendars')
-        return res
+                childs.append('Calendars')
+        return childs
 
-    def get_resourcetype(self, uri, cache=None):
+    @classmethod
+    def get_resourcetype(cls, uri, cache=None):
         from pywebdav.lib.constants import COLLECTION, OBJECT
         if uri in ('Calendars', 'Calendars/'):
             return COLLECTION
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 return COLLECTION
             return OBJECT
-        elif self.calendar(uri, ics=True):
+        elif cls.calendar(uri, ics=True):
             return OBJECT
-        return super(Collection, self).get_resourcetype(uri, cache=cache)
+        return super(Collection, cls).get_resourcetype(uri, cache=cache)
 
-    def get_displayname(self, uri, cache=None):
-        calendar_obj = Pool().get('calendar.calendar')
+    @classmethod
+    def get_displayname(cls, uri, cache=None):
+        Calendar = Pool().get('calendar.calendar')
         if uri in ('Calendars', 'Calendars/'):
             return 'Calendars'
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
-                return calendar_obj.browse(calendar_id).rec_name
+                return Calendar(calendar_id).rec_name
             return uri.split('/')[-1]
-        elif self.calendar(uri, ics=True):
+        elif cls.calendar(uri, ics=True):
             return uri.split('/')[-1]
-        return super(Collection, self).get_displayname(uri, cache=cache)
+        return super(Collection, cls).get_displayname(uri, cache=cache)
 
-    def get_contenttype(self, uri, cache=None):
-        if self.event(uri) \
-                or self.calendar(uri, ics=True):
+    @classmethod
+    def get_contenttype(cls, uri, cache=None):
+        if cls.event(uri) \
+                or cls.calendar(uri, ics=True):
             return 'text/calendar'
-        return super(Collection, self).get_contenttype(uri, cache=cache)
+        return super(Collection, cls).get_contenttype(uri, cache=cache)
 
-    def get_creationdate(self, uri, cache=None):
-        calendar_obj = Pool().get('calendar.calendar')
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def get_creationdate(cls, uri, cache=None):
+        Calendar = Pool().get('calendar.calendar')
+        Event = Pool().get('calendar.event')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if not calendar_id:
-            calendar_id = self.calendar(uri, ics=True)
+            calendar_id = cls.calendar(uri, ics=True)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 if cache is not None:
                     cache.setdefault('_calendar', {})
-                    cache['_calendar'].setdefault(calendar_obj._name, {})
-                    ids = cache['_calendar'][calendar_obj._name].keys()
+                    cache['_calendar'].setdefault(Calendar.__name__, {})
+                    ids = cache['_calendar'][Calendar.__name__].keys()
                     if calendar_id not in ids:
                         ids.append(calendar_id)
                     elif 'creationdate' in cache['_calendar'][
-                            calendar_obj._name][calendar_id]:
-                        return cache['_calendar'][calendar_obj._name][
+                            Calendar.__name__][calendar_id]:
+                        return cache['_calendar'][Calendar.__name__][
                             calendar_id]['creationdate']
                 else:
                     ids = [calendar_id]
@@ -285,30 +287,30 @@
                     red_sql, red_ids = reduce_ids('id', sub_ids)
                     cursor.execute('SELECT id, ' \
                                 'EXTRACT(epoch FROM create_date) ' \
-                            'FROM "' + calendar_obj._table + '" ' \
+                            'FROM "' + Calendar._table + '" ' \
                             'WHERE ' + red_sql, red_ids)
                     for calendar_id2, date in cursor.fetchall():
                         if calendar_id2 == calendar_id:
                             res = date
                         if cache is not None:
-                            cache['_calendar'][calendar_obj._name]\
+                            cache['_calendar'][Calendar.__name__]\
                                     .setdefault(calendar_id2, {})
-                            cache['_calendar'][calendar_obj._name][
+                            cache['_calendar'][Calendar.__name__][
                                 calendar_id2]['creationdate'] = date
                 if res is not None:
                     return res
             else:
-                event_id = self.event(uri, calendar_id=calendar_id)
+                event_id = cls.event(uri, calendar_id=calendar_id)
                 if event_id:
                     if cache is not None:
                         cache.setdefault('_calendar', {})
-                        cache['_calendar'].setdefault(event_obj._name, {})
-                        ids = cache['_calendar'][event_obj._name].keys()
+                        cache['_calendar'].setdefault(Event.__name__, {})
+                        ids = cache['_calendar'][Event.__name__].keys()
                         if event_id not in ids:
                             ids.append(event_id)
                         elif 'creationdate' in cache['_calendar'][
-                                event_obj._name][event_id]:
-                            return cache['_calendar'][event_obj._name][
+                                Event.__name__][event_id]:
+                            return cache['_calendar'][Event.__name__][
                                 event_id]['creationdate']
                     else:
                         ids = [event_id]
@@ -319,37 +321,39 @@
                         red_sql, red_ids = reduce_ids('id', sub_ids)
                         cursor.execute('SELECT id, ' \
                                 'EXTRACT(epoch FROM create_date) ' \
-                            'FROM "' + event_obj._table + '" ' \
+                            'FROM "' + Event._table + '" ' \
                             'WHERE ' + red_sql, red_ids)
                         for event_id2, date in cursor.fetchall():
                             if event_id2 == event_id:
                                 res = date
                             if cache is not None:
-                                cache['_calendar'][event_obj._name]\
+                                cache['_calendar'][Event.__name__]\
                                         .setdefault(event_id2, {})
-                                cache['_calendar'][event_obj._name][
+                                cache['_calendar'][Event.__name__][
                                     event_id2]['creationdate'] = date
                     if res is not None:
                         return res
-        return super(Collection, self).get_creationdate(uri, cache=cache)
+        return super(Collection, cls).get_creationdate(uri, cache=cache)
 
-    def get_lastmodified(self, uri, cache=None):
-        calendar_obj = Pool().get('calendar.calendar')
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def get_lastmodified(cls, uri, cache=None):
+        pool = Pool()
+        Calendar = pool.get('calendar.calendar')
+        Event = pool.get('calendar.event')
 
         cursor = Transaction().cursor
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 if cache is not None:
                     cache.setdefault('_calendar', {})
-                    cache['_calendar'].setdefault(calendar_obj._name, {})
-                    ids = cache['_calendar'][calendar_obj._name].keys()
+                    cache['_calendar'].setdefault(Calendar.__name__, {})
+                    ids = cache['_calendar'][Calendar.__name__].keys()
                     if calendar_id not in ids:
                         ids.append(calendar_id)
                     elif 'lastmodified' in cache['_calendar'][
-                            calendar_obj._name][calendar_id]:
-                        return cache['_calendar'][calendar_obj._name][
+                            Calendar.__name__][calendar_id]:
+                        return cache['_calendar'][Calendar.__name__][
                             calendar_id]['lastmodified']
                 else:
                     ids = [calendar_id]
@@ -360,30 +364,30 @@
                     cursor.execute('SELECT id, ' \
                                 'EXTRACT(epoch FROM ' \
                                 'COALESCE(write_date, create_date)) ' \
-                            'FROM "' + calendar_obj._table + '" ' \
+                            'FROM "' + Calendar.__table__ + '" ' \
                                 'WHERE ' + red_sql, red_ids)
                     for calendar_id2, date in cursor.fetchall():
                         if calendar_id2 == calendar_id:
                             res = date
                         if cache is not None:
-                            cache['_calendar'][calendar_obj._name]\
+                            cache['_calendar'][Calendar.__name__]\
                                     .setdefault(calendar_id2, {})
-                            cache['_calendar'][calendar_obj._name][
+                            cache['_calendar'][Calendar.__name__][
                                 calendar_id2]['lastmodified'] = date
                 if res is not None:
                     return res
             else:
-                event_id = self.event(uri, calendar_id=calendar_id)
+                event_id = cls.event(uri, calendar_id=calendar_id)
                 if event_id:
                     if cache is not None:
                         cache.setdefault('_calendar', {})
-                        cache['_calendar'].setdefault(event_obj._name, {})
-                        ids = cache['_calendar'][event_obj._name].keys()
+                        cache['_calendar'].setdefault(Event.__name__, {})
+                        ids = cache['_calendar'][Event.__name__].keys()
                         if event_id not in ids:
                             ids.append(event_id)
                         elif 'lastmodified' in cache['_calendar'][
-                                event_obj._name][event_id]:
-                            return cache['_calendar'][event_obj._name][
+                                Event.__name__][event_id]:
+                            return cache['_calendar'][Event.__name__][
                                 event_id]['lastmodified']
                     else:
                         ids = [event_id]
@@ -396,7 +400,7 @@
                         cursor.execute('SELECT COALESCE(parent, id), ' \
                                     'MAX(EXTRACT(epoch FROM ' \
                                     'COALESCE(write_date, create_date))) ' \
-                                'FROM "' + event_obj._table + '" ' \
+                                'FROM "' + Event._table + '" ' \
                                 'WHERE ' + red_id_sql + ' ' \
                                     'OR ' + red_parent_sql + ' ' \
                                 'GROUP BY parent, id',
@@ -405,23 +409,23 @@
                             if event_id2 == event_id:
                                 res = date
                             if cache is not None:
-                                cache['_calendar'][event_obj._name]\
+                                cache['_calendar'][Event.__name__]\
                                         .setdefault(event_id2, {})
-                                cache['_calendar'][event_obj._name][
+                                cache['_calendar'][Event.__name__][
                                     event_id2]['lastmodified'] = date
                     if res is not None:
                         return res
-        calendar_ics_id = self.calendar(uri, ics=True)
+        calendar_ics_id = cls.calendar(uri, ics=True)
         if calendar_ics_id:
             if cache is not None:
                 cache.setdefault('_calendar', {})
-                cache['_calendar'].setdefault(calendar_obj._name, {})
-                ids = cache['_calendar'][calendar_obj._name].keys()
+                cache['_calendar'].setdefault(Calendar.__name__, {})
+                ids = cache['_calendar'][Calendar.__name__].keys()
                 if calendar_ics_id not in ids:
                     ids.append(calendar_ics_id)
                 elif 'lastmodified ics' in cache['_calendar'][
-                        calendar_obj._name][calendar_ics_id]:
-                    return cache['_calendar'][calendar_obj._name][
+                        Calendar.__name__][calendar_ics_id]:
+                    return cache['_calendar'][Calendar.__name__][
                         calendar_ics_id]['lastmodified ics']
             else:
                 ids = [calendar_ics_id]
@@ -431,200 +435,210 @@
                 red_sql, red_ids = reduce_ids('calendar', sub_ids)
                 cursor.execute('SELECT calendar, MAX(EXTRACT(epoch FROM ' \
                             'COALESCE(write_date, create_date))) ' \
-                        'FROM "' + event_obj._table + '" ' \
+                        'FROM "' + Event._table + '" ' \
                         'WHERE ' + red_sql + ' ' \
                         'GROUP BY calendar', red_ids)
                 for calendar_id2, date in cursor.fetchall():
                     if calendar_id2 == calendar_ics_id:
                         res = date
                     if cache is not None:
-                        cache['_calendar'][calendar_obj._name]\
+                        cache['_calendar'][Calendar.__name__]\
                                 .setdefault(calendar_id2, {})
-                        cache['_calendar'][calendar_obj._name][
+                        cache['_calendar'][Calendar.__name__][
                             calendar_id2]['lastmodified ics'] = date
             if res is not None:
                 return res
-        return super(Collection, self).get_lastmodified(uri, cache=cache)
+        return super(Collection, cls).get_lastmodified(uri, cache=cache)
 
-    def get_data(self, uri, cache=None):
-        event_obj = Pool().get('calendar.event')
-        calendar_obj = Pool().get('calendar.calendar')
+    @classmethod
+    def get_data(cls, uri, cache=None):
+        pool = Pool()
+        Event = pool.get('calendar.event')
+        Calendar = pool.get('calendar.calendar')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 raise DAV_NotFound
-            event_id = self.event(uri, calendar_id=calendar_id)
+            event_id = cls.event(uri, calendar_id=calendar_id)
             if not event_id:
                 raise DAV_NotFound
-            ical = event_obj.event2ical(event_id)
+            ical = Event.event2ical(event_id)
             return ical.serialize()
-        calendar_ics_id = self.calendar(uri, ics=True)
+        calendar_ics_id = cls.calendar(uri, ics=True)
         if calendar_ics_id:
-            ical = calendar_obj.calendar2ical(calendar_ics_id)
+            ical = Calendar.calendar2ical(calendar_ics_id)
             return ical.serialize()
-        return super(Collection, self).get_data(uri, cache=cache)
+        return super(Collection, cls).get_data(uri, cache=cache)
 
-    def get_calendar_description(self, uri, cache=None):
-        calendar_obj = Pool().get('calendar.calendar')
+    @classmethod
+    def get_calendar_description(cls, uri, cache=None):
+        Calendar = Pool().get('calendar.calendar')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 if cache is not None:
                     cache.setdefault('_calendar', {})
-                    cache['_calendar'].setdefault(calendar_obj._name, {})
-                    ids = cache['_calendar'][calendar_obj._name].keys()
+                    cache['_calendar'].setdefault(Calendar.__name__, {})
+                    ids = cache['_calendar'][Calendar.__name__].keys()
                     if calendar_id not in ids:
                         ids.append(calendar_id)
                     elif 'calendar_description' in cache['_calendar'][
-                            calendar_obj._name][calendar_id]:
-                        res = cache['_calendar'][calendar_obj._name][
+                            Calendar.__name__][calendar_id]:
+                        res = cache['_calendar'][Calendar.__name__][
                             calendar_id]['calendar_description']
                         if res is not None:
                             return res
                 else:
                     ids = [calendar_id]
                 res = None
-                for calendar in calendar_obj.browse(ids):
+                for calendar in Calendar.browse(ids):
                     if calendar.id == calendar_id:
                         res = calendar.description
                     if cache is not None:
-                        cache['_calendar'][calendar_obj._name]\
+                        cache['_calendar'][Calendar.__name__]\
                                 .setdefault(calendar.id, {})
-                        cache['_calendar'][calendar_obj._name][
+                        cache['_calendar'][Calendar.__name__][
                             calendar.id]['calendar_description'] = \
                                 calendar.description
                 if res is not None:
                     return res
         raise DAV_NotFound
 
-    def get_calendar_data(self, uri, cache=None):
-        return self.get_data(uri, cache=cache).decode('utf-8')
+    @classmethod
+    def get_calendar_data(cls, uri, cache=None):
+        return cls.get_data(uri, cache=cache).decode('utf-8')
 
-    def get_calendar_home_set(self, uri, cache=None):
+    @staticmethod
+    def get_calendar_home_set(uri, cache=None):
         return '/Calendars'
 
-    def get_calendar_user_address_set(self, uri, cache=None):
-        user_obj = Pool().get('res.user')
-        user = user_obj.browse(Transaction().user)
+    @staticmethod
+    def get_calendar_user_address_set(uri, cache=None):
+        User = Pool().get('res.user')
+        user = User(Transaction().user)
         if user.email:
             return user.email
         raise DAV_NotFound
 
-    def get_schedule_inbox_URL(self, uri, cache=None):
-        calendar_obj = Pool().get('calendar.calendar')
+    @staticmethod
+    def get_schedule_inbox_URL(uri, cache=None):
+        Calendar = Pool().get('calendar.calendar')
         user = Transaction().user
 
-        calendar_ids = calendar_obj.search([
+        calendars = Calendar.search([
             ('owner', '=', user),
             ], limit=1)
-        if not calendar_ids:
+        if not calendars:
             # Sunbird failed with no value
             return '/Calendars'
-        calendar = calendar_obj.browse(calendar_ids[0])
+        calendar, = calendars
         return '/Calendars/' + calendar.name
 
-    def get_schedule_outbox_URL(self, uri, cache=None):
-        return self.get_schedule_inbox_URL(uri, cache=cache)
+    @classmethod
+    def get_schedule_outbox_URL(cls, uri, cache=None):
+        return cls.get_schedule_inbox_URL(uri, cache=cache)
 
-    def put(self, uri, data, content_type, cache=None):
-        event_obj = Pool().get('calendar.event')
-        calendar_obj = Pool().get('calendar.calendar')
+    @classmethod
+    def put(cls, uri, data, content_type, cache=None):
+        pool = Pool()
+        Event = pool.get('calendar.event')
+        Calendar = pool.get('calendar.calendar')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 raise DAV_Forbidden
-            event_id = self.event(uri, calendar_id=calendar_id)
+            event_id = cls.event(uri, calendar_id=calendar_id)
             if not event_id:
                 ical = vobject.readOne(data)
-                values = event_obj.ical2values(None, ical, calendar_id)
-                event_id = event_obj.create(values)
-                event = event_obj.browse(event_id)
-                calendar = calendar_obj.browse(calendar_id)
+                values = Event.ical2values(None, ical, calendar_id)
+                event = Event.create(values)
+                calendar = Calendar(calendar_id)
                 return (Transaction().cursor.database_name + '/Calendars/' +
                         calendar.name + '/' + event.uuid + '.ics')
             else:
                 ical = vobject.readOne(data)
-                values = event_obj.ical2values(event_id, ical, calendar_id)
-                event_obj.write(event_id, values)
+                values = Event.ical2values(event_id, ical, calendar_id)
+                Event.write([Event(event_id)], values)
                 return
-        calendar_ics_id = self.calendar(uri, ics=True)
+        calendar_ics_id = cls.calendar(uri, ics=True)
         if calendar_ics_id:
             raise DAV_Forbidden
-        return super(Collection, self).put(uri, data, content_type)
+        return super(Collection, cls).put(uri, data, content_type)
 
-    def mkcol(self, uri, cache=None):
+    @classmethod
+    def mkcol(cls, uri, cache=None):
         if uri and uri.startswith('Calendars/'):
             raise DAV_Forbidden
-        return super(Collection, self).mkcol(uri, cache=cache)
+        return super(Collection, cls).mkcol(uri, cache=cache)
 
-    def rmcol(self, uri, cache=None):
-        calendar_obj = Pool().get('calendar.calendar')
+    @classmethod
+    def rmcol(cls, uri, cache=None):
+        Calendar = Pool().get('calendar.calendar')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 try:
-                    calendar_obj.delete(calendar_id)
+                    Calendar.delete([Calendar(calendar_id)])
                 except Exception:
                     raise DAV_Forbidden
                 return 200
             raise DAV_Forbidden
-        return super(Collection, self).rmcol(uri, cache=cache)
+        return super(Collection, cls).rmcol(uri, cache=cache)
 
-    def rm(self, uri, cache=None):
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def rm(cls, uri, cache=None):
+        Event = Pool().get('calendar.event')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 return 403
-            event_id = self.event(uri, calendar_id=calendar_id)
+            event_id = cls.event(uri, calendar_id=calendar_id)
             if event_id:
                 try:
-                    event_obj.delete(event_id)
+                    Event.delete([Event(event_id)])
                 except Exception:
                     return 403
                 return 200
             return 404
-        calendar_ics_id = self.calendar(uri, ics=True)
+        calendar_ics_id = cls.calendar(uri, ics=True)
         if calendar_ics_id:
             return 403
-        return super(Collection, self).rm(uri, cache=cache)
+        return super(Collection, cls).rm(uri, cache=cache)
 
-    def exists(self, uri, cache=None):
+    @classmethod
+    def exists(cls, uri, cache=None):
         if uri in ('Calendars', 'Calendars/'):
             return 1
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 return 1
-            if self.event(uri, calendar_id=calendar_id):
+            if cls.event(uri, calendar_id=calendar_id):
                 return 1
-        calendar_ics_id = self.calendar(uri, ics=True)
+        calendar_ics_id = cls.calendar(uri, ics=True)
         if calendar_ics_id:
             return 1
-        return super(Collection, self).exists(uri, cache=cache)
+        return super(Collection, cls).exists(uri, cache=cache)
 
-    def current_user_privilege_set(self, uri, cache=None):
+    @classmethod
+    def current_user_privilege_set(cls, uri, cache=None):
         '''
         Return the privileges of the current user for uri
         Privileges ares: create, read, write, delete
-
-        :param uri: the uri
-        :param cache: the cache
-        :return: a list of privileges
         '''
-        calendar_obj = Pool().get('calendar.calendar')
+        Calendar = Pool().get('calendar.calendar')
 
         if uri in ('Calendars', 'Calendars/'):
             return ['create', 'read', 'write', 'delete']
         if uri and uri.startswith('Calendars/'):
-            calendar_id = self.calendar(uri)
+            calendar_id = cls.calendar(uri)
             if calendar_id:
-                calendar = calendar_obj.browse(calendar_id)
+                calendar = Calendar(calendar_id)
                 user = Transaction().user
                 if user == calendar.owner.id:
                     return ['create', 'read', 'write', 'delete']
@@ -635,7 +649,5 @@
                     res.extend(['read', 'write', 'delete'])
                 return res
             return []
-        return super(Collection, self).current_user_privilege_set(uri,
-                cache=cache)
-
-Collection()
+        return super(Collection, cls).current_user_privilege_set(uri,
+            cache=cache)
diff -r 74d42794032d trytond/modules/calendar_classification/__init__.py
--- a/trytond/modules/calendar_classification/__init__.py	Wed Jul 25 20:48:29 2012 +0200
+++ b/trytond/modules/calendar_classification/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,4 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .calendar_ import *
+
+
+def register():
+    Pool.register(
+        Event,
+        module='calendar_classification', type_='model')
diff -r 74d42794032d trytond/modules/calendar_classification/calendar_.py
--- a/trytond/modules/calendar_classification/calendar_.py	Wed Jul 25 20:48:29 2012 +0200
+++ b/trytond/modules/calendar_classification/calendar_.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,23 +1,29 @@
 #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 vobject
-from trytond.model import ModelSQL
+
 from trytond.tools import reduce_ids
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
 
-class Event(ModelSQL):
-    _name = 'calendar.event'
+__all__ = ['Event']
+__metaclass__ = PoolMeta
 
-    def __init__(self):
-        super(Event, self).__init__()
-        self._error_messages.update({
+
+class Event:
+    __name__ = 'calendar.event'
+
+    @classmethod
+    def __setup__(cls):
+        super(Event, cls).__setup__()
+        cls._error_messages.update({
             'transparent': 'Free',
             'opaque': 'Busy',
             })
 
-    def search(self, domain, offset=0, limit=None, order=None, count=False,
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False,
             query_string=False):
         if Transaction().user:
             domain = domain[:]
@@ -33,23 +39,22 @@
                     ('classification', '!=', 'confidential'),
                     ],
                 ]
-        return super(Event, self).search(domain, offset=offset, limit=limit,
-                order=order, count=count, query_string=query_string)
+        return super(Event, cls).search(domain, offset=offset, limit=limit,
+            order=order, count=count, query_string=query_string)
 
-    def create(self, values):
-        new_id = super(Event, self).create(values)
-        if self.search([('id', '=', new_id)], count=True) != 1:
-            self.raise_user_error('access_error', self._description)
-        return new_id
+    @classmethod
+    def create(cls, values):
+        event = super(Event, cls).create(values)
+        if cls.search([('id', '=', event.id)], count=True) != 1:
+            cls.raise_user_error('access_error', cls.__doc__)
+        return event
 
-    def _clean_private(self, record, transp):
+    @classmethod
+    def _clean_private(cls, record, transp):
         '''
         Clean private record
-
-        :param record: a dictionary with record values
-        :param transp: the time transparency
         '''
-        summary = self.raise_user_error(transp, raise_exception=False)
+        summary = cls.raise_user_error(transp, raise_exception=False)
         if 'summary' in record:
             record['summary'] = summary
 
@@ -79,26 +84,23 @@
         if vevent:
             record['vevent'] = vevent.serialize()
 
-    def read(self, ids, fields_names=None):
-        rule_obj = Pool().get('ir.rule')
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        Rule = Pool().get('ir.rule')
         cursor = Transaction().cursor
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-        if len({}.fromkeys(ids)) != self.search([('id', 'in', ids)],
+        if len(set(ids)) != cls.search([('id', 'in', ids)],
                 count=True):
-            self.raise_user_error('access_error', self._description)
+            cls.raise_user_error('access_error', cls.__doc__)
 
         writable_ids = []
-        domain1, domain2 = rule_obj.domain_get(self._name, mode='write')
+        domain1, domain2 = Rule.domain_get(cls.__name__, mode='write')
         if domain1:
             for i in range(0, len(ids), cursor.IN_MAX):
                 sub_ids = ids[i:i + cursor.IN_MAX]
                 red_sql, red_ids = reduce_ids('id', sub_ids)
-                cursor.execute('SELECT id FROM "' + self._table + '" ' \
-                        'WHERE ' + red_sql + ' AND (' + domain1 + ')',
-                        red_ids + domain2)
+                cursor.execute('SELECT id FROM "' + cls._table + '" '
+                    'WHERE ' + red_sql + ' AND (' + domain1 + ')',
+                    red_ids + domain2)
                 writable_ids.extend(x[0] for x in cursor.fetchall())
         else:
             writable_ids = ids
@@ -112,35 +114,28 @@
             if field not in fields_names:
                 fields_names.append(field)
                 to_remove.add(field)
-        res = super(Event, self).read(ids, fields_names=fields_names)
+        res = super(Event, cls).read(ids, fields_names=fields_names)
         for record in res:
             if record['classification'] == 'private' \
                     and record['id'] not in writable_ids:
-                self._clean_private(record, record['transp'])
+                cls._clean_private(record, record['transp'])
             for field in to_remove:
                 del record[field]
-        if int_id:
-            return res[0]
         return res
 
-    def write(self, ids, values):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        if len({}.fromkeys(ids)) != self.search([('id', 'in', ids)],
+    @classmethod
+    def write(cls, events, values):
+        if len(set(events)) != cls.search([('id', 'in', map(int, events))],
                 count=True):
-            self.raise_user_error('access_error', self._description)
-        res = super(Event, self).write(ids, values)
-        if len({}.fromkeys(ids)) != self.search([('id', 'in', ids)],
+            cls.raise_user_error('access_error', cls.__doc__)
+        super(Event, cls).write(events, values)
+        if len(set(events)) != cls.search([('id', 'in', map(int, events))],
                 count=True):
-            self.raise_user_error('access_error', self._description)
-        return res
+            cls.raise_user_error('access_error', cls.__doc__)
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        if len({}.fromkeys(ids)) != self.search([('id', 'in', ids)],
+    @classmethod
+    def delete(cls, events):
+        if len(set(events)) != cls.search([('id', 'in', map(int, events))],
                 count=True):
-            self.raise_user_error('access_error', self._description)
-        return super(Event, self).delete(ids)
-
-Event()
+            cls.raise_user_error('access_error', cls.__doc__)
+        super(Event, cls).delete(events)
diff -r 189608575f90 trytond/modules/calendar_scheduling/__init__.py
--- a/trytond/modules/calendar_scheduling/__init__.py	Sat Aug 11 11:56:24 2012 +0200
+++ b/trytond/modules/calendar_scheduling/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,6 +1,16 @@
 #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.pool import Pool
 from . import caldav
 from .calendar_ import *
 from .res import *
+
+
+def register():
+    Pool.register(
+        Event,
+        Attendee,
+        EventAttendee,
+        User,
+        module='calendar_scheduling', type_='model')
diff -r 189608575f90 trytond/modules/calendar_scheduling/calendar_.py
--- a/trytond/modules/calendar_scheduling/calendar_.py	Sat Aug 11 11:56:24 2012 +0200
+++ b/trytond/modules/calendar_scheduling/calendar_.py	Mon Sep 24 20:36:40 2012 +0200
@@ -3,15 +3,18 @@
 from email.mime.base import MIMEBase
 from email.mime.multipart import MIMEMultipart
 import logging
-from trytond.model import ModelSQL, ModelView, fields
+
+from trytond.model import fields
 from trytond.tools import get_smtp_server
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Event', 'Attendee', 'EventAttendee']
+__metaclass__ = PoolMeta
 
-class Event(ModelSQL, ModelView):
-    _name = 'calendar.event'
 
+class Event:
+    __name__ = 'calendar.event'
     organizer_schedule_status = fields.Selection([
             ('', ''),
             ('1.0', '1.0'),
@@ -30,12 +33,14 @@
             ('CLIENT', 'Client'),
             ], 'Schedule Agent')
 
-    def default_schedule_agent(self):
+    @staticmethod
+    def default_schedule_agent():
         return 'SERVER'
 
-    def __init__(self):
-        super(Event, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Event, cls).__setup__()
+        cls._error_messages.update({
             'new_subject': 'Invitation: %s @ %s',
             'new_body': 'You have been invited to the following event.\n\n',
             'update_subject': 'Updated Invitation: %s @ %s',
@@ -48,9 +53,10 @@
             'when': 'When',
             })
 
-    def ical2values(self, event_id, ical, calendar_id, vevent=None):
-        res = super(Event, self).ical2values(event_id, ical, calendar_id,
-                vevent=vevent)
+    @classmethod
+    def ical2values(cls, event_id, ical, calendar_id, vevent=None):
+        res = super(Event, cls).ical2values(event_id, ical, calendar_id,
+            vevent=vevent)
 
         if not vevent:
             vevent = ical.vevent
@@ -61,13 +67,14 @@
         for key in ('status', 'agent'):
             field = 'organizer_schedule_' + key
             param = 'SCHEDULE-' + key.upper()
-            if param in vevent.organizer.params and vevent.organizer[param] \
-                    in dict(self[field].selection):
+            if (param in vevent.organizer.params
+                    and vevent.organizer[param] in dict(getattr(cls,
+                            field).selection)):
                 res[field] = vevent.organizer[param]
 
         return res
 
-    def event2ical(self, event):
+    def event2ical(self):
         """
         Override default event2ical to add schedule-status and
         schedule-agent properties
@@ -78,63 +85,57 @@
         scheduling message.
         """
 
-        ical = super(Event, self).event2ical(event)
-        if isinstance(event, (int, long)):
-            event = self.browse(event)
-
+        ical = super(Event, self).event2ical()
         vevent = ical.vevent
 
-        if event.organizer_schedule_status:
+        if self.organizer_schedule_status:
             if not hasattr(vevent, 'organizer'):
                 vevent.add('organizer')
             vevent.organizer.params['SCHEDULE-STATUS'] = \
-                (event.organizer_schedule_status,)
+                (self.organizer_schedule_status,)
 
         if Transaction().context.get('skip_schedule_agent'):
             return ical
 
-        if event.organizer_schedule_agent:
+        if self.organizer_schedule_agent:
             if not hasattr(vevent, 'organizer'):
                 vevent.add('organizer')
             vevent.organizer.params['SCHEDULE-AGENT'] = \
-                event.organizer_schedule_agent
+                self.organizer_schedule_agent
 
         return ical
 
-    def subject_body(self, type, event, owner):
-        lang_obj = Pool().get('ir.lang')
+    def subject_body(self, type, owner):
+        Lang = Pool().get('ir.lang')
 
-        if not (event and owner):
+        if not owner:
             return "", ""
         lang = owner.language
         if not lang:
-            lang_ids = lang_obj.search([
-                ('code', '=', 'en_US'),
-                ], limit=1)
-            lang = lang_obj.browse(lang_ids[0])
+            lang, = Lang.search([
+                    ('code', '=', 'en_US'),
+                    ], limit=1)
 
         with Transaction().set_context(language=lang.code):
-            summary = event.summary
+            summary = self.summary
             if not summary:
                 summary = self.raise_user_error('no_subject',
                         raise_exception=False)
 
-        date = lang_obj.strftime(event.dtstart, lang.code, lang.date)
-        if not event.all_day:
-            date += ' ' + lang_obj.strftime(event.dtstart, lang.code, '%H:%M')
-            if event.dtend:
+        date = Lang.strftime(self.dtstart, lang.code, lang.date)
+        if not self.all_day:
+            date += ' ' + Lang.strftime(self.dtstart, lang.code, '%H:%M')
+            if self.dtend:
                 date += ' -'
-                if event.dtstart.date() != event.dtend.date():
-                    date += ' ' + lang_obj.strftime(event.dtend, lang.code,
-                            lang.date)
-                date += ' ' + lang_obj.strftime(event.dtend, lang.code,
-                        '%H:%M')
+                if self.dtstart.date() != self.dtend.date():
+                    date += ' ' + Lang.strftime(self.dtend, lang.code,
+                        lang.date)
+                date += ' ' + Lang.strftime(self.dtend, lang.code, '%H:%M')
         else:
-            if event.dtend and event.dtstart.date() != event.dtend.date():
-                date += ' - ' + lang_obj.strftime(event.dtend, lang.code,
-                        lang.date)
-        if event.timezone:
-            date += ' ' + event.timezone
+            if self.dtend and self.dtstart.date() != self.dtend.date():
+                date += ' - ' + Lang.strftime(self.dtend, lang.code, lang.date)
+        if self.timezone:
+            date += ' ' + self.timezone
 
         with Transaction().set_context(language=lang.code):
             subject = self.raise_user_error(type + '_subject',
@@ -157,22 +158,22 @@
                 if lang.direction == 'ltr':
                     body += fields['attendees']['string'] + separator + '\n'
                     body += bullet + owner.email + '\n'
-                    for attendee in event.attendees:
+                    for attendee in self.attendees:
                         body += bullet + attendee.email + '\n'
                 else:
                     body += separator + fields['attendees']['string'] + '\n'
                     body += owner.email + bullet + '\n'
-                    for attendee in event.attendees:
+                    for attendee in self.attendees:
                         body += attendee.email + bullet + '\n'
-            elif event[field]:
+            elif getattr(self, field):
                 if field == 'summary':
                     value = summary
                 elif field == 'dtstart':
                     value = date
                 elif field == 'location':
-                    value = event.location.name
+                    value = self.location.name
                 else:
-                    value = event[field]
+                    value = getattr(self, field)
                 if lang.direction == 'ltr':
                     body += fields[field]['string'] + separator + ' ' \
                             + value + '\n'
@@ -181,7 +182,8 @@
                             + fields[field]['string'] + '\n'
         return subject, body
 
-    def create_msg(self, from_addr, to_addrs, subject, body, ical=None):
+    @staticmethod
+    def create_msg(from_addr, to_addrs, subject, body, ical=None):
 
         if not to_addrs:
             return None
@@ -215,28 +217,21 @@
 
         return msg
 
-    def send_msg(self, from_addr, to_addrs, msg, type, event_id):
+    def send_msg(self, from_addr, to_addrs, msg, type):
         '''
-        Send message
-
-        :param from_addr: a from-address string
-        :param to_addrs: a list of to-address strings
-        :param msg: a message string
-        :param type: the type (new, update, cancel)
-        :param event_id: the id of the calendar.event
-        :return: list of email addresses sent
+        Send message and return the list of email addresses sent
         '''
-        user_obj = Pool().get('res.user')
+        User = Pool().get('res.user')
 
         if not to_addrs:
             return to_addrs
         to_addrs = list(set(to_addrs))
 
-        user_ids = user_obj.search([
-            ('email', 'in', to_addrs),
-            ])
-        for user in user_obj.browse(user_ids):
-            if not user['calendar_email_notification_' + type]:
+        users = User.search([
+                ('email', 'in', to_addrs),
+                ])
+        for user in users:
+            if not getattr(user, 'calendar_email_notification_' + type):
                 to_addrs.remove(user.email)
 
         success = False
@@ -247,24 +242,23 @@
             success = to_addrs
         except Exception:
             logging.getLogger('calendar_scheduling').error(
-                'Unable to deliver scheduling mail for event %s' % event_id)
+                'Unable to deliver scheduling mail for %s' % self)
         return success
 
-    @staticmethod
-    def attendees_to_notify(event):
-        if not event.calendar.owner:
+    def attendees_to_notify(self):
+        if not self.calendar.owner:
             return [], None
-        attendees = event.attendees
-        organizer = event.organizer
-        owner = event.calendar.owner
-        if event.parent:
+        attendees = self.attendees
+        organizer = self.organizer
+        owner = self.calendar.owner
+        if self.parent:
             if not attendees:
-                attendees = event.parent.attendees
-                organizer = event.parent.organizer
-                owner = event.parent.calendar.owner
+                attendees = self.parent.attendees
+                organizer = self.parent.organizer
+                owner = self.parent.calendar.owner
             elif not organizer:
-                organizer = event.parent.organizer
-                owner = event.parent.calendar.owner
+                organizer = self.parent.organizer
+                owner = self.parent.calendar.owner
 
         if organizer != owner.email:
             return [], None
@@ -273,61 +267,58 @@
         for attendee in attendees:
             if attendee.email == organizer:
                 continue
-            if attendee.schedule_agent and\
-                    attendee.schedule_agent != 'SERVER':
+            if (attendee.schedule_agent
+                    and attendee.schedule_agent != 'SERVER'):
                 continue
             to_notify.append(attendee)
 
         return to_notify, owner
 
-    def create(self, values):
-        attendee_obj = Pool().get('calendar.event.attendee')
-        res = super(Event, self).create(values)
+    @classmethod
+    def create(cls, values):
+        Attendee = Pool().get('calendar.event.attendee')
+        event = super(Event, cls).create(values)
 
         if Transaction().user == 0:
             # user is 0 means create is triggered by another one
-            return res
+            return event
 
-        event = self.browse(res)
-
-        to_notify, owner = self.attendees_to_notify(event)
+        to_notify, owner = event.attendees_to_notify()
         if not to_notify:
-            return res
+            return event
 
         with Transaction().set_context(skip_schedule_agent=True):
-            ical = self.event2ical(event)
+            ical = event.event2ical()
         ical.add('method')
         ical.method.value = 'REQUEST'
 
         attendee_emails = [a.email for a in to_notify]
 
-        subject, body = self.subject_body('new', event, owner)
-        msg = self.create_msg(owner.email, attendee_emails, subject, body,
-                ical)
-        sent = self.send_msg(owner.email, attendee_emails, msg, 'new',
-                event.id)
+        subject, body = event.subject_body('new', owner)
+        msg = cls.create_msg(owner.email, attendee_emails, subject, body, ical)
+        sent = event.send_msg(owner.email, attendee_emails, msg, 'new')
 
         vals = {'status': 'needs-action'}
         if sent:
             vals['schedule_status'] = '1.1'  # successfully sent
         else:
             vals['schedule_status'] = '5.1'  # could not complete delivery
-        attendee_obj.write([a.id for a in to_notify], vals)
+        Attendee.write(to_notify, vals)
 
-        return res
+        return event
 
-    def write(self, ids, values):
-        attendee_obj = Pool().get('calendar.event.attendee')
+    @classmethod
+    def write(cls, events, values):
+        Attendee = Pool().get('calendar.event.attendee')
 
         if Transaction().user == 0:
             # user is 0 means write is triggered by another one
-            return super(Event, self).write(ids, values)
+            super(Event, cls).write(events, values)
+            return
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        if not values or not ids:
-            return super(Event, self).write(ids, values)
+        if not values or not events:
+            super(Event, cls).write(events, values)
+            return
 
         event_edited = False
         for k in values:
@@ -336,23 +327,20 @@
                 break
 
         # store old attendee info
-        events = self.browse(ids)
         event2former_emails = {}
         former_organiser_mail = {}
         former_organiser_lang = {}
         for event in events:
-            to_notify, owner = self.attendees_to_notify(event)
+            to_notify, owner = event.attendees_to_notify()
             event2former_emails[event.id] = [a.email for a in to_notify]
             former_organiser_mail[event.id] = owner and owner.email
             former_organiser_lang[event.id] = owner and owner.language \
                 and owner.language.code
 
-        res = super(Event, self).write(ids, values)
-
-        events = self.browse(ids)
+        super(Event, cls).write(events, values)
 
         for event in events:
-            current_attendees, owner = self.attendees_to_notify(event)
+            current_attendees, owner = event.attendees_to_notify()
             owner_email = owner and owner.email
             current_emails = [a.email for a in current_attendees]
             former_emails = event2former_emails.get(event.id, [])
@@ -361,22 +349,22 @@
 
             if missing_mails:
                 with Transaction().set_context(skip_schedule_agent=True):
-                    ical = self.event2ical(event)
+                    ical = event.event2ical()
                 ical.add('method')
                 ical.method.value = 'CANCEL'
 
-                subject, body = self.subject_body('cancel', event, owner)
-                msg = self.create_msg(former_organiser_mail[event.id],
-                        missing_mails, subject, body, ical)
-                sent = self.send_msg(former_organiser_mail[event.id],
-                        missing_mails, msg, 'cancel', event.id)
+                subject, body = event.subject_body('cancel', owner)
+                msg = cls.create_msg(former_organiser_mail[event.id],
+                    missing_mails, subject, body, ical)
+                sent = event.send_msg(former_organiser_mail[event.id],
+                    missing_mails, msg, 'cancel')
 
             new_attendees = filter(lambda a: a.email not in former_emails,
                 current_attendees)
             old_attendees = filter(lambda a: a.email in former_emails,
                 current_attendees)
             with Transaction().set_context(skip_schedule_agent=True):
-                ical = self.event2ical(event)
+                ical = event.event2ical()
             if not hasattr(ical, 'method'):
                 ical.add('method')
 
@@ -386,13 +374,13 @@
                 if event.status == 'cancelled':
                     ical.method.value = 'CANCEL'
                     #send cancel to old attendee
-                    subject, body = self.subject_body('cancel', event, owner)
-                    msg = self.create_msg(owner_email,
-                            [a.email for a in old_attendees],
-                            subject, body, ical)
-                    sent = self.send_msg(owner_email,
-                            [a.email for a in old_attendees],
-                            msg, 'cancel', event.id)
+                    subject, body = event.subject_body('cancel', owner)
+                    msg = cls.create_msg(owner_email,
+                        [a.email for a in old_attendees],
+                        subject, body, ical)
+                    sent = event.send_msg(owner_email,
+                        [a.email for a in old_attendees],
+                        msg, 'cancel')
                     if sent:
                         sent_succes += old_attendees
                     else:
@@ -401,25 +389,25 @@
                 else:
                     ical.method.value = 'REQUEST'
                     #send update to old attendees
-                    subject, body = self.subject_body('update', event, owner)
-                    msg = self.create_msg(owner_email,
-                            [a.email for a in old_attendees],
-                            subject, body, ical)
-                    sent = self.send_msg(owner_email,
-                            [a.email for a in old_attendees],
-                            msg, 'update', event.id)
+                    subject, body = event.subject_body('update', owner)
+                    msg = cls.create_msg(owner_email,
+                        [a.email for a in old_attendees],
+                        subject, body, ical)
+                    sent = event.send_msg(owner_email,
+                        [a.email for a in old_attendees],
+                        msg, 'update')
                     if sent:
                         sent_succes += old_attendees
                     else:
                         sent_fail += old_attendees
                     #send new to new attendees
-                    subject, body = self.subject_body('new', event, owner)
-                    msg = self.create_msg(owner_email,
-                            [a.email for a in new_attendees],
-                            subject, body, ical)
-                    sent = self.send_msg(owner_email,
-                            [a.email for a in new_attendees],
-                            msg, 'new', event.id)
+                    subject, body = event.subject_body('new', owner)
+                    msg = cls.create_msg(owner_email,
+                        [a.email for a in new_attendees],
+                        subject, body, ical)
+                    sent = event.send_msg(owner_email,
+                        [a.email for a in new_attendees],
+                        msg, 'new')
                     if sent:
                         sent_succes += new_attendees
                     else:
@@ -429,13 +417,13 @@
                 if event.status != 'cancelled':
                     ical.method.value = 'REQUEST'
                     #send new to new attendees
-                    subject, body = self.subject_body('new', event, owner)
-                    msg = self.create_msg(owner_email,
-                            [a.email for a in new_attendees],
-                            subject, body, ical)
-                    sent = self.send_msg(owner_email,
-                            [a.email for a in new_attendees],
-                            msg, 'new', event.id)
+                    subject, body = event.subject_body('new', owner)
+                    msg = cls.create_msg(owner_email,
+                        [a.email for a in new_attendees],
+                        subject, body, ical)
+                    sent = event.send_msg(owner_email,
+                        [a.email for a in new_attendees],
+                        msg, 'new')
                     if sent:
                         sent_succes += new_attendees
                     else:
@@ -443,53 +431,45 @@
 
                 vals = {'status': 'needs-action'}
                 vals['schedule_status'] = '1.1'  # successfully sent
-                attendee_obj.write([a.id for a in sent_succes], vals)
+                Attendee.write(sent_succes, vals)
                 vals['schedule_status'] = '5.1'  # could not complete delivery
-                attendee_obj.write([a.id for a in sent_fail], vals)
-        return res
+                Attendee.write(sent_fail, vals)
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, events):
         if Transaction().user == 0:
             # user is 0 means the deletion is triggered by another one
-            return super(Event, self).delete(ids)
+            super(Event, cls).delete(events)
+            return
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        events = self.browse(ids)
         send_list = []
         for event in events:
             if event.status == 'cancelled':
                 continue
-            to_notify, owner = self.attendees_to_notify(event)
+            to_notify, owner = event.attendees_to_notify()
             if not to_notify:
                 continue
 
             with Transaction().set_context(skip_schedule_agent=True):
-                ical = self.event2ical(event)
+                ical = event.event2ical()
             ical.add('method')
             ical.method.value = 'CANCEL'
 
             attendee_emails = [a.email for a in to_notify]
-            subject, body = self.subject_body('cancel', event, owner)
-            msg = self.create_msg(owner.email, attendee_emails, subject, body,
-                    ical)
+            subject, body = event.subject_body('cancel', owner)
+            msg = cls.create_msg(owner.email, attendee_emails, subject, body,
+                ical)
 
-            send_list.append((owner.email, attendee_emails, msg, event.id,))
+            send_list.append((owner.email, attendee_emails, msg, event))
 
-        res = super(Event, self).delete(ids)
+        super(Event, cls).delete(events)
         for args in send_list:
-            owner_email, attendee_emails, msg, event_id = args
-            self.send_msg(owner_email, attendee_emails, msg, 'cancel',
-                    event_id)
-        return res
+            owner_email, attendee_emails, msg, event = args
+            event.send_msg(owner_email, attendee_emails, msg, 'cancel')
 
-Event()
 
-
-class Attendee(ModelSQL, ModelView):
-    _name = 'calendar.attendee'
-
+class Attendee:
+    __name__ = 'calendar.attendee'
     schedule_status = fields.Selection([
             ('', ''),
             ('1.0', '1.0'),
@@ -508,60 +488,62 @@
             ('CLIENT', 'Client'),
             ], 'Schedule Agent')
 
-    def default_schedule_agent(self):
+    @classmethod
+    def default_schedule_agent():
         return 'SERVER'
 
-    def attendee2values(self, attendee):
-        res = super(Attendee, self).attendee2values(attendee)
+    @classmethod
+    def attendee2values(cls, attendee):
+        values = super(Attendee, cls).attendee2values(attendee)
         if hasattr(attendee, 'schedule_status'):
             if attendee.schedule_status in dict(
-                    self.schedule_status.selection):
-                res['schedule_status'] = attendee.schedule_status
+                    cls.schedule_status.selection):
+                values['schedule_status'] = attendee.schedule_status
         if hasattr(attendee, 'schedule_agent'):
-            if attendee.schedule_agent in dict(self.schedule_agent.selection):
-                res['schedule_agent'] = attendee.schedule_agent
-        return res
+            if attendee.schedule_agent in dict(cls.schedule_agent.selection):
+                values['schedule_agent'] = attendee.schedule_agent
+        return values
 
-    def attendee2attendee(self, attendee):
-        res = super(Attendee, self).attendee2attendee(attendee)
+    def attendee2attendee(self):
+        attendee = super(Attendee, self).attendee2attendee()
 
-        if attendee.schedule_status:
-            if hasattr(res, 'schedule_status_param'):
-                if res.schedule_status_param in dict(
-                        self.schedule_status.selection):
-                    res.schedule_status_param = attendee.schedule_status
+        if self.schedule_status:
+            if hasattr(attendee, 'schedule_status_param'):
+                if attendee.schedule_status_param in dict(
+                        self.__class__.schedule_status.selection):
+                    attendee.schedule_status_param = self.schedule_status
             else:
-                res.schedule_status_param = attendee.schedule_status
-        elif hasattr(res, 'schedule_status_param'):
-            if res.schedule_status_param in dict(
-                    self.schedule_status.selection):
-                del res.schedule_status_param
+                attendee.schedule_status_param = self.schedule_status
+        elif hasattr(attendee, 'schedule_status_param'):
+            if attendee.schedule_status_param in dict(
+                    self.__class__.schedule_status.selection):
+                del attendee.schedule_status_param
 
         if Transaction().context.get('skip_schedule_agent'):
-            return res
+            return attendee
 
-        if attendee.schedule_agent:
-            if hasattr(res, 'schedule_agent_param'):
-                if res.schedule_agent_param in dict(
-                        self.schedule_agent.selection):
-                    res.schedule_agent_param = attendee.schedule_agent
+        if self.schedule_agent:
+            if hasattr(attendee, 'schedule_agent_param'):
+                if attendee.schedule_agent_param in dict(
+                        self.__class__.schedule_agent.selection):
+                    attendee.schedule_agent_param = self.schedule_agent
             else:
-                res.schedule_agent_param = attendee.schedule_agent
-        elif hasattr(res, 'schedule_agent_param'):
-            if res.schedule_agent_param in dict(self.schedule_agent.selection):
-                del res.schedule_agent_param
+                attendee.schedule_agent_param = self.schedule_agent
+        elif hasattr(attendee, 'schedule_agent_param'):
+            if attendee.schedule_agent_param in dict(
+                    self.__class__.schedule_agent.selection):
+                del attendee.schedule_agent_param
 
-        return res
+        return attendee
 
-Attendee()
 
+class EventAttendee:
+    __name__ = 'calendar.event.attendee'
 
-class EventAttendee(ModelSQL, ModelView):
-    _name = 'calendar.event.attendee'
-
-    def __init__(self):
-        super(EventAttendee, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(EventAttendee, cls).__setup__()
+        cls._error_messages.update({
                 'subject': '%s: %s @ %s',
                 'body': ('%s (%s) changed his/her participation status '
                     'to: %s\n\n'),
@@ -573,18 +555,19 @@
                 'when': 'When',
                 })
 
-    def subject_body(self, status, event, owner):
-        lang_obj = Pool().get('ir.lang')
-        event_obj = Pool().get('calendar.event')
+    def subject_body(self, status, owner):
+        pool = Pool()
+        Lang = pool.get('ir.lang')
+        Event = pool.get('calendar.event')
+        event = self.event
 
         if not (event and owner):
             return "", ""
         lang = owner.language
         if not lang:
-            lang_ids = lang_obj.search([
-                ('code', '=', 'en_US'),
-                ], limit=1)
-            lang = lang_obj.browse(lang_ids[0])
+            lang, = Lang.search([
+                    ('code', '=', 'en_US'),
+                    ], limit=1)
 
         summary = event.summary
         if not summary:
@@ -592,20 +575,19 @@
                 summary = self.raise_user_error('no_subject',
                         raise_exception=False)
 
-        date = lang_obj.strftime(event.dtstart, lang.code, lang.date)
+        date = Lang.strftime(event.dtstart, lang.code, lang.date)
         if not event.all_day:
-            date += ' ' + lang_obj.strftime(event.dtstart, lang.code, '%H:%M')
+            date += ' ' + Lang.strftime(event.dtstart, lang.code, '%H:%M')
             if event.dtend:
                 date += ' -'
                 if event.dtstart.date() != event.dtend.date():
-                    date += ' ' + lang_obj.strftime(event.dtend, lang.code,
-                            lang.date)
-                date += ' ' + lang_obj.strftime(event.dtend, lang.code,
-                        '%H:%M')
+                    date += ' ' + Lang.strftime(event.dtend, lang.code,
+                        lang.date)
+                date += ' ' + Lang.strftime(event.dtend, lang.code, '%H:%M')
         else:
             if event.dtend and event.dtstart.date() != event.dtend.date():
-                date += ' - ' + lang_obj.strftime(event.dtend, lang.code,
-                        lang.date)
+                date += ' - ' + Lang.strftime(event.dtend, lang.code,
+                    lang.date)
         if event.timezone:
             date += ' ' + event.timezone
 
@@ -634,7 +616,7 @@
             bullet = self.raise_user_error('bullet', raise_exception=False)
 
             fields_names = ['summary', 'dtstart', 'location', 'attendees']
-            fields = event_obj.fields_get(fields_names=fields_names)
+            fields = Event.fields_get(fields_names=fields_names)
             fields['dtstart']['string'] = self.raise_user_error('when',
                     raise_exception=False)
         for field in fields_names:
@@ -669,7 +651,8 @@
                             + fields[field]['string'] + '\n'
         return subject, body
 
-    def create_msg(self, from_addr, to_addr, subject, body, ical=None):
+    @staticmethod
+    def create_msg(from_addr, to_addr, subject, body, ical=None):
 
         if not to_addr:
             return None
@@ -702,15 +685,9 @@
 
         return msg
 
-    def send_msg(self, from_addr, to_addr, msg, event_id):
+    def send_msg(self, from_addr, to_addr, msg):
         '''
-        Send message
-
-        :param from_addr: a from-address string
-        :param to_addr: recipient address
-        :param msg: a message string
-        :param event_id: the id of the calendar.event
-        :return: a boolean (True if the mail has been sent)
+        Send message and return True if the mail has been sent
         '''
         success = False
         try:
@@ -720,12 +697,11 @@
             success = True
         except Exception:
             logging.getLogger('calendar_scheduling').error(
-                    'Unable to deliver reply mail for event %s' % event_id)
+                'Unable to deliver reply mail for %s' % self)
         return success
 
-    @staticmethod
-    def organiser_to_notify(attendee):
-        event = attendee.event
+    def organiser_to_notify(self):
+        event = self.event
         organizer = event.organizer or event.parent and event.parent.organizer
         if not organizer:
             return None
@@ -734,37 +710,35 @@
             return None
         if organizer == event.calendar.owner.email:
             return None
-        if attendee.email != event.calendar.owner.email:
+        if self.email != event.calendar.owner.email:
             return None
 
         return organizer
 
-    def write(self, ids, values):
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def write(cls, attendees, values):
+        Event = Pool().get('calendar.event')
 
         if Transaction().user == 0:
             # user is 0 means write is triggered by another one
-            return super(EventAttendee, self).write(ids, values)
+            super(EventAttendee, cls).write(attendees, values)
+            return
 
         if 'status' not in values:
-            return super(EventAttendee, self).write(ids, values)
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+            super(EventAttendee, cls).write(attendees, values)
+            return
 
         att2status = {}
-        attendees = self.browse(ids)
         for attendee in attendees:
             att2status[attendee.id] = attendee.status
 
-        res = super(EventAttendee, self).write(ids, values)
+        super(EventAttendee, cls).write(attendees, values)
 
-        attendees = self.browse(ids)
         for attendee in attendees:
             owner = attendee.event.calendar.owner
             if not owner or not owner.calendar_email_notification_partstat:
                 continue
-            organizer = self.organiser_to_notify(attendee)
+            organizer = attendee.organiser_to_notify()
             if not organizer:
                 continue
 
@@ -775,32 +749,28 @@
                 continue
 
             with Transaction().set_context(skip_schedule_agent=True):
-                ical = event_obj.event2ical(attendee.event)
+                ical = attendee.event.event2ical()
             if not hasattr(ical, 'method'):
                 ical.add('method')
             ical.method.value = 'REPLY'
 
-            subject, body = self.subject_body(new, attendee.event, owner)
-            msg = self.create_msg(owner.email, organizer, subject, body, ical)
+            subject, body = attendee.subject_body(new, owner)
+            msg = cls.create_msg(owner.email, organizer, subject, body, ical)
 
-            sent = self.send_msg(owner.email, organizer, msg,
-                    attendee.event.id)
+            sent = attendee.send_msg(owner.email, organizer, msg)
 
             vals = {'organizer_schedule_status': sent and '1.1' or '5.1'}
-            event_obj.write(attendee.event.id, vals)
+            Event.write([attendee.event], vals)
 
-        return res
-
-    def delete(self, ids):
-        event_obj = Pool().get('calendar.event')
+    @classmethod
+    def delete(cls, attendees):
+        Event = Pool().get('calendar.event')
 
         if Transaction().user == 0:
             # user is 0 means the deletion is triggered by another one
-            return super(EventAttendee, self).delete(ids)
+            super(EventAttendee, cls).delete(attendees)
+            return
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        attendees = self.browse(ids)
         send_list = []
         for attendee in attendees:
             owner = attendee.event.calendar.owner
@@ -809,66 +779,59 @@
                 continue
             if not owner or not owner.calendar_email_notification_partstat:
                 continue
-            organizer = self.organiser_to_notify(attendee)
+            organizer = attendee.organiser_to_notify()
             if not organizer:
                 continue
 
             with Transaction().set_context(skip_schedule_agent=True):
-                ical = event_obj.event2ical(attendee.event)
+                ical = attendee.event.event2ical()
             if not hasattr(ical, 'method'):
                 ical.add('method')
             ical.method.value = 'REPLY'
 
-            subject, body = self.subject_body('declined', attendee.event,
-                    owner)
-            msg = self.create_msg(owner.email, organizer, subject, body, ical)
+            subject, body = attendee.subject_body('declined', owner)
+            msg = cls.create_msg(owner.email, organizer, subject, body, ical)
 
-            send_list.append((owner.email, organizer, msg, attendee.event.id))
+            send_list.append((owner.email, organizer, msg, attendee))
 
-        res = super(EventAttendee, self).delete(ids)
+        super(EventAttendee, cls).delete(attendees)
         for args in send_list:
-            owner_email, organizer, msg, event_id = args
-            sent = self.send_msg(owner_email, organizer, msg, event_id)
+            owner_email, organizer, msg, attendee = args
+            sent = attendee.send_msg(owner_email, organizer, msg)
             vals = {'organizer_schedule_status': sent and '1.1' or '5.1'}
-            event_obj.write(event_id, vals)
+            Event.write([attendee.event], vals)
 
-        return res
+    @classmethod
+    def create(cls, values):
+        Event = Pool().get('calendar.event')
 
-    def create(self, values):
-        event_obj = Pool().get('calendar.event')
-
-        res_id = super(EventAttendee, self).create(values)
+        attendee = super(EventAttendee, cls).create(values)
         if Transaction().user == 0:
             # user is 0 means create is triggered by another one
-            return res_id
-
-        attendee = self.browse(res_id)
+            return attendee
 
         owner = attendee.event.calendar.owner
 
         if (not attendee.status) or attendee.status in ('', 'needs-action'):
-            return res_id
+            return attendee
         if not owner or not owner.calendar_email_notification_partstat:
-            return res_id
-        organizer = self.organiser_to_notify(attendee)
+            return attendee
+        organizer = attendee.organiser_to_notify()
         if not organizer:
-            return res_id
+            return attendee
 
         with Transaction().set_context(skip_schedule_agent=True):
-            ical = event_obj.event2ical(attendee.event)
+            ical = attendee.event.event2ical()
         if not hasattr(ical, 'method'):
             ical.add('method')
         ical.method.value = 'REPLY'
 
-        subject, body = self.subject_body(attendee.status, attendee.event,
-                owner)
-        msg = self.create_msg(owner.email, organizer, subject, body, ical)
+        subject, body = attendee.subject_body(attendee.status, owner)
+        msg = cls.create_msg(owner.email, organizer, subject, body, ical)
 
-        sent = self.send_msg(owner.email, organizer, msg, attendee.event.id)
+        sent = attendee.send_msg(owner.email, organizer, msg)
 
         vals = {'organizer_schedule_status': sent and '1.1' or '5.1'}
-        event_obj.write(attendee.event.id, vals)
+        Event.write([attendee.event], vals)
 
-        return res_id
-
-EventAttendee()
+        return attendee
diff -r 189608575f90 trytond/modules/calendar_scheduling/res.py
--- a/trytond/modules/calendar_scheduling/res.py	Sat Aug 11 11:56:24 2012 +0200
+++ b/trytond/modules/calendar_scheduling/res.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,11 +1,14 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from trytond.model import ModelSQL, ModelView, fields
+from trytond.model import fields
+from trytond.pool import PoolMeta
 
+__all__ = ['User']
+__metaclass__ = PoolMeta
 
-class User(ModelSQL, ModelView):
-    _name = 'res.user'
 
+class User:
+    __name__ = 'res.user'
     calendar_email_notification_new = fields.Boolean(
             'New invitations')
     calendar_email_notification_update = fields.Boolean(
@@ -15,25 +18,28 @@
     calendar_email_notification_partstat = fields.Boolean(
             'Invitation Replies')
 
-    def default_calendar_email_notification_new(self):
+    @staticmethod
+    def default_calendar_email_notification_new():
         return True
 
-    def default_calendar_email_notification_update(self):
+    @staticmethod
+    def default_calendar_email_notification_update():
         return True
 
-    def default_calendar_email_notification_cancel(self):
+    @staticmethod
+    def default_calendar_email_notification_cancel():
         return True
 
-    def default_calendar_email_notification_partstat(self):
+    @staticmethod
+    def default_calendar_email_notification_partstat():
         return True
 
-    def __init__(self):
-        super(User, self).__init__()
-        self._preferences_fields += [
+    @classmethod
+    def __setup__(cls):
+        super(User, cls).__setup__()
+        cls._preferences_fields += [
             'calendar_email_notification_new',
             'calendar_email_notification_update',
             'calendar_email_notification_cancel',
             'calendar_email_notification_partstat',
-        ]
-
-User()
+            ]
diff -r 873d6d76778e trytond/modules/calendar_todo/__init__.py
--- a/trytond/modules/calendar_todo/__init__.py	Wed Sep 19 22:19:45 2012 +0200
+++ b/trytond/modules/calendar_todo/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,6 +1,21 @@
 #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.pool import Pool
 from . import caldav
 from .todo import *
 from .webdav import *
+
+
+def register():
+    Pool.register(
+        Todo,
+        TodoCategory,
+        TodoRDate,
+        TodoRRule,
+        TodoExDate,
+        TodoExRule,
+        TodoAttendee,
+        TodoAlarm,
+        Collection,
+        module='calendar_todo', type_='model')
diff -r 873d6d76778e trytond/modules/calendar_todo/todo.py
--- a/trytond/modules/calendar_todo/todo.py	Wed Sep 19 22:19:45 2012 +0200
+++ b/trytond/modules/calendar_todo/todo.py	Mon Sep 24 20:36:40 2012 +0200
@@ -13,6 +13,9 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Todo', 'TodoCategory', 'TodoRDate', 'TodoRRule', 'TodoExDate',
+    'TodoExRule', 'TodoAttendee', 'TodoAlarm']
+
 tzlocal = dateutil.tz.tzlocal()
 tzutc = dateutil.tz.tzutc()
 
@@ -21,10 +24,8 @@
 
 class Todo(ModelSQL, ModelView):
     "Todo"
-    _description = __doc__
-    _name = 'calendar.todo'
+    __name__ = 'calendar.todo'
     _rec_name = 'uuid'
-
     calendar = fields.Many2One('calendar.calendar', 'Calendar',
             required=True, select=True, ondelete="CASCADE")
     alarms = fields.One2Many('calendar.todo.alarm', 'todo', 'Alarms')
@@ -110,102 +111,106 @@
         'Write Users'), 'get_calendar_field', searcher='search_calendar_field')
     vtodo = fields.Binary('vtodo')
 
-    def __init__(self):
-        super(Todo, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Todo, cls).__setup__()
+        cls._sql_constraints = [
             #XXX should be unique across all componenets
             ('uuid_recurrence_uniq', 'UNIQUE(uuid, calendar, recurrence)',
                 'UUID and recurrence must be unique in a calendar!'),
-        ]
-        self._constraints += [
+            ]
+        cls._constraints += [
             ('check_recurrence', 'invalid_recurrence'),
-        ]
-        self._error_messages.update({
-            'invalid_recurrence': 'Recurrence can not be recurrent!',
-        })
+            ]
+        cls._error_messages.update({
+                'invalid_recurrence': 'Recurrence can not be recurrent!',
+                })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
+        pool = Pool()
         # Migrate from 1.4: remove classification_public
-        model_data_obj = Pool().get('ir.model.data')
-        rule_obj = Pool().get('ir.rule')
+        ModelData = pool.get('ir.model.data')
+        Rule = pool.get('ir.rule')
         with Transaction().set_user(0):
-            model_data_ids = model_data_obj.search([
+            models_data = ModelData.search([
                 ('fs_id', '=', 'rule_group_read_todo_line3'),
                 ('module', '=', module_name),
                 ('inherit', '=', None),
                 ], limit=1)
-            if model_data_ids:
-                model_data = model_data_obj.browse(model_data_ids[0])
-                rule_obj.delete(model_data.db_id)
-        return super(Todo, self).init(module_name)
+            if models_data:
+                model_data, = models_data
+                Rule.delete([Rule(model_data.db_id)])
+        super(Todo, cls).__register__(module_name)
 
-    def default_uuid(self):
+    @staticmethod
+    def default_uuid():
         return str(uuid.uuid4())
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return 0
 
-    def default_classification(self):
+    @staticmethod
+    def default_classification():
         return 'public'
 
-    def default_timezone(self):
-        user_obj = Pool().get('res.user')
-        user = user_obj.browse(Transaction().user)
+    @staticmethod
+    def default_timezone():
+        User = Pool().get('res.user')
+        user = User(Transaction().user)
         return user.timezone
 
-    def default_percent_complete(self):
+    @staticmethod
+    def default_percent_complete():
         return 0
 
-    def on_change_status(self, vals):
+    def on_change_status(self):
         res = {}
-        if 'status' not in vals:
+        if not getattr(self, 'status', None):
             return res
-        if vals['status'] == 'completed':
+        if self.status == 'completed':
             res['percent_complete'] = 100
-            if not vals.get('completed'):
+            if not getattr(self, 'completed', None):
                 res['completed'] = datetime.datetime.now()
 
         return res
 
-    def timezones(self):
+    @staticmethod
+    def timezones():
         return [(x, x) for x in pytz.common_timezones] + [('', '')]
 
-    def get_calendar_field(self, ids, name):
-        assert name in ('calendar_owner', 'calendar_read_users',
-                'calendar_write_users'), 'Invalid name'
-        res = {}
-        for todo in self.browse(ids):
-            name = name[9:]
-            if name in ('read_users', 'write_users'):
-                res[todo.id] = [x.id for x in todo.calendar[name]]
-            else:
-                res[todo.id] = todo.calendar[name].id
-        return res
+    def get_calendar_field(self, name):
+        name = name[9:]
+        if name in ('read_users', 'write_users'):
+            return [x.id for x in getattr(self.calendar, name)]
+        else:
+            return getattr(self.calendar, name).id
 
     def search_calendar_field(self, name, clause):
         return [('calendar.' + name[9:],) + tuple(clause[1:])]
 
-    def check_recurrence(self, ids):
+    def check_recurrence(self):
         '''
         Check the recurrence is not recurrent.
         '''
-        for todo in self.browse(ids):
-            if not todo.parent:
-                continue
-            if todo.rdates \
-                    or todo.rrules \
-                    or todo.exdates \
-                    or todo.exrules \
-                    or todo.occurences:
-                return False
+        if not self.parent:
+            return True
+        if (self.rdates
+                or self.rrules
+                or self.exdates
+                or self.exrules
+                or self.occurences):
+            return False
         return True
 
-    def create(self, values):
-        calendar_obj = Pool().get('calendar.calendar')
-        collection_obj = Pool().get('webdav.collection')
+    @classmethod
+    def create(cls, values):
+        pool = Pool()
+        Calendar = pool.get('calendar.calendar')
+        Collection = pool.get('webdav.collection')
 
-        res = super(Todo, self).create(values)
-        todo = self.browse(res)
+        todo = super(Todo, cls).create(values)
         if (todo.calendar.owner
                 and (todo.organizer == todo.calendar.owner.email
                     or (todo.parent
@@ -221,73 +226,69 @@
                         and x.email != todo.parent.organizer]
             if attendee_emails:
                 with Transaction().set_user(0):
-                    calendar_ids = calendar_obj.search([
+                    calendars = Calendar.search([
                         ('owner.email', 'in', attendee_emails),
                         ])
                     if not todo.recurrence:
-                        for calendar_id in calendar_ids:
-                            new_id = self.copy(todo.id, default={
-                                'calendar': calendar_id,
+                        for calendar in calendars:
+                            new_todo = cls.copy([todo], default={
+                                'calendar': calendar.id,
                                 'occurences': None,
                                 })
                             for occurence in todo.occurences:
-                                self.copy(occurence.id, default={
-                                    'calendar': calendar_id,
-                                    'parent': new_id,
+                                cls.copy([occurence], default={
+                                    'calendar': calendar.id,
+                                    'parent': new_todo.id,
                                     })
                     else:
-                        parent_ids = self.search([
+                        parents = cls.search([
                             ('uuid', '=', todo.uuid),
                             ('calendar.owner.email', 'in', attendee_emails),
                             ('id', '!=', todo.id),
                             ('recurrence', '=', None),
                             ])
-                        for parent in self.browse(parent_ids):
-                            self.copy(todo.id, default={
+                        for parent in parents:
+                            cls.copy([todo], default={
                                 'calendar': parent.calendar.id,
                                 'parent': parent.id,
                                 })
         # Restart the cache for todo
-        collection_obj.todo.reset()
-        return res
+        Collection._todo_cache.clear()
+        return todo
 
-    def _todo2update(self, todo):
-        pool = Pool()
-        rdate_obj = pool.get('calendar.todo.rdate')
-        exdate_obj = pool.get('calendar.todo.exdate')
-        rrule_obj = pool.get('calendar.todo.rrule')
-        exrule_obj = pool.get('calendar.todo.exrule')
-
+    def _todo2update(self):
         res = {}
-        res['summary'] = todo.summary
-        res['description'] = todo.description
-        res['dtstart'] = todo.dtstart
-        res['percent_complete'] = todo.percent_complete
-        res['completed'] = todo.completed
-        res['location'] = todo.location.id
-        res['status'] = todo.status
-        res['organizer'] = todo.organizer
+        res['summary'] = self.summary
+        res['description'] = self.description
+        res['dtstart'] = self.dtstart
+        res['percent_complete'] = self.percent_complete
+        res['completed'] = self.completed
+        res['location'] = self.location.id
+        res['status'] = self.status
+        res['organizer'] = self.organizer
         res['rdates'] = [('delete_all',)]
-        for rdate in todo.rdates:
-            vals = rdate_obj._date2update(rdate)
+        for rdate in self.rdates:
+            vals = rdate._date2update()
             res['rdates'].append(('create', vals))
         res['exdates'] = [('delete_all',)]
-        for exdate in todo.exdates:
-            vals = exdate_obj._date2update(exdate)
+        for exdate in self.exdates:
+            vals = exdate._date2update()
             res['exdates'].append(('create', vals))
         res['rrules'] = [('delete_all',)]
-        for rrule in todo.rrules:
-            vals = rrule_obj._rule2update(rrule)
+        for rrule in self.rrules:
+            vals = rrule._rule2update()
             res['rrules'].append(('create', vals))
         res['exrules'] = [('delete_all',)]
-        for exrule in todo.exrules:
-            vals = exrule_obj._rule2update(exrule)
+        for exrule in self.exrules:
+            vals = exrule._rule2update()
             res['exrules'].append(('create', vals))
         return res
 
-    def write(self, ids, values):
-        calendar_obj = Pool().get('calendar.calendar')
-        collection_obj = Pool().get('webdav.collection')
+    @classmethod
+    def write(cls, todos, values):
+        pool = Pool()
+        Calendar = pool.get('calendar.calendar')
+        Collection = pool.get('webdav.collection')
 
         cursor = Transaction().cursor
 
@@ -295,19 +296,17 @@
         if 'sequence' in values:
             del values['sequence']
 
-        res = super(Todo, self).write(ids, values)
+        super(Todo, cls).write(todos, values)
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
+        ids = [t.id for t in todos]
         for i in range(0, len(ids), cursor.IN_MAX):
             sub_ids = ids[i:i + cursor.IN_MAX]
             red_sql, red_ids = reduce_ids('id', sub_ids)
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET sequence = sequence + 1 ' \
                     'WHERE ' + red_sql, red_ids)
 
-        for todo in self.browse(ids):
+        for todo in todos:
             if todo.calendar.owner \
                     and (todo.organizer == todo.calendar.owner.email \
                     or (todo.parent \
@@ -322,57 +321,56 @@
                             and x.email != todo.parent.organizer]
                 if attendee_emails:
                     with Transaction().set_user(0):
-                        todo_ids = self.search([
+                        todo2s = cls.search([
                             ('uuid', '=', todo.uuid),
                             ('calendar.owner.email', 'in', attendee_emails),
                             ('id', '!=', todo.id),
                             ('recurrence', '=', todo.recurrence),
                             ])
-                    for todo2 in self.browse(todo_ids):
+                    for todo2 in todo2s:
                         if todo2.calendar.owner.email in attendee_emails:
                             attendee_emails.remove(todo2.calendar.owner.email)
                     with Transaction().set_user(0):
-                        self.write(todo_ids, self._todo2update(todo))
+                        cls.write(todos, todo._todo2update())
                 if attendee_emails:
                     with Transaction().set_user(0):
-                        calendar_ids = calendar_obj.search([
+                        calendars = Calendar.search([
                             ('owner.email', 'in', attendee_emails),
                             ])
                         if not todo.recurrence:
-                            for calendar_id in calendar_ids:
-                                new_id = self.copy(todo.id, default={
-                                    'calendar': calendar_id,
+                            for calendar in calendars:
+                                new_todo, = cls.copy([todo], default={
+                                    'calendar': calendar.id,
                                     'occurences': None,
                                     })
                                 for occurence in todo.occurences:
-                                    self.copy(occurence.id, default={
-                                        'calendar': calendar_id,
-                                        'parent': new_id,
+                                    cls.copy([occurence], default={
+                                        'calendar': calendar.id,
+                                        'parent': new_todo.id,
                                         })
                         else:
-                            parent_ids = self.search([
+                            parents = cls.search([
                                     ('uuid', '=', todo.uuid),
                                     ('calendar.owner.email', 'in',
                                         attendee_emails),
                                     ('id', '!=', todo.id),
                                     ('recurrence', '=', None),
                                     ])
-                            for parent in self.browse(parent_ids):
-                                self.copy(todo.id, default={
+                            for parent in parents:
+                                cls.copy([todo], default={
                                     'calendar': parent.calendar.id,
                                     'parent': parent.id,
                                     })
         # Restart the cache for todo
-        collection_obj.todo.reset()
-        return res
+        Collection._todo_cache.clear()
 
-    def delete(self, ids):
-        attendee_obj = Pool().get('calendar.todo.attendee')
-        collection_obj = Pool().get('webdav.collection')
+    @classmethod
+    def delete(cls, todos):
+        pool = Pool()
+        Attendee = pool.get('calendar.todo.attendee')
+        Collection = pool.get('webdav.collection')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for todo in self.browse(ids):
+        for todo in todos:
             if todo.calendar.owner \
                     and (todo.organizer == todo.calendar.owner.email \
                     or (todo.parent \
@@ -385,13 +383,13 @@
                             if x.email != todo.parent.organizer]
                 if attendee_emails:
                     with Transaction().set_user(0):
-                        todo_ids = self.search([
+                        todos_delete = cls.search([
                             ('uuid', '=', todo.uuid),
                             ('calendar.owner.email', 'in', attendee_emails),
                             ('id', '!=', todo.id),
                             ('recurrence', '=', todo.recurrence),
                             ])
-                        self.delete(todo_ids)
+                        cls.delete(todos_delete)
             elif todo.organizer \
                     or (todo.parent and todo.parent.organizer):
                 if todo.organizer:
@@ -399,62 +397,54 @@
                 else:
                     organizer = todo.parent.organizer
                 with Transaction().set_user(0):
-                    todo_ids = self.search([
+                    todo2s = cls.search([
                         ('uuid', '=', todo.uuid),
                         ('calendar.owner.email', '=', organizer),
                         ('id', '!=', todo.id),
                         ('recurrence', '=', todo.recurrence),
                         ], limit=1)
-                    if todo_ids:
-                        todo2 = self.browse(todo_ids[0])
+                    if todo2s:
+                        todo2, = todo2s
                         for attendee in todo2.attendees:
                             if attendee.email == todo.calendar.owner.email:
-                                attendee_obj.write(attendee.id, {
+                                Attendee.write([attendee], {
                                     'status': 'declined',
                                     })
-        res = super(Todo, self).delete(ids)
+        super(Todo, cls).delete(todos)
         # Restart the cache for todo
-        collection_obj.todo.reset()
-        return res
+        Collection._todo_cache.clear()
 
-    def copy(self, ids, default=None):
-        int_id = isinstance(ids, (int, long))
-        if int_id:
-            ids = [ids]
-
+    @classmethod
+    def copy(cls, todos, default=None):
         if default is None:
             default = {}
 
-        new_ids = []
-        for todo_id in ids:
+        new_todos = []
+        for todo in todos:
             current_default = default.copy()
-            current_default['uuid'] = self.default_uuid()
-            new_id = super(Todo, self).copy(todo_id, default=current_default)
-            new_ids.append(new_id)
+            current_default['uuid'] = cls.default_uuid()
+            new_todo, = super(Todo, cls).copy([todo], default=current_default)
+            new_todos.append(new_todo)
+        return new_todos
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-    def ical2values(self, todo_id, ical, calendar_id, vtodo=None):
+    @classmethod
+    def ical2values(cls, todo_id, ical, calendar_id, vtodo=None):
         '''
-        Convert iCalendar to values for create or write
-
-        :param todo_id: the todo id for write or None for create
-        :param ical: a ical instance of vobject
-        :param calendar_id: the calendar id of the todo
-        :param vtodo: the vtodo of the ical to use if None use the first one
-        :return: a dictionary with values
+        Convert iCalendar to values for create or write with:
+        todo_id: the todo id for write or None for create
+        ical: a ical instance of vobject
+        calendar_id: the calendar id of the todo
+        vtodo: the vtodo of the ical to use if None use the first one
         '''
         pool = Pool()
-        category_obj = pool.get('calendar.category')
-        location_obj = pool.get('calendar.location')
-        alarm_obj = pool.get('calendar.todo.alarm')
-        attendee_obj = pool.get('calendar.todo.attendee')
-        rdate_obj = pool.get('calendar.todo.rdate')
-        exdate_obj = pool.get('calendar.todo.exdate')
-        rrule_obj = pool.get('calendar.todo.rrule')
-        exrule_obj = pool.get('calendar.todo.exrule')
+        Category = pool.get('calendar.category')
+        Location = pool.get('calendar.location')
+        Alarm = pool.get('calendar.todo.alarm')
+        Attendee = pool.get('calendar.todo.attendee')
+        Rdate = pool.get('calendar.todo.rdate')
+        Exdate = pool.get('calendar.todo.exdate')
+        Rrule = pool.get('calendar.todo.rrule')
+        Exrule = pool.get('calendar.todo.exrule')
 
         vtodos = []
         if not vtodo:
@@ -467,7 +457,7 @@
 
         todo = None
         if todo_id:
-            todo = self.browse(todo_id)
+            todo = cls(todo_id)
         res = {}
         if not todo:
             if hasattr(vtodo, 'uid'):
@@ -535,40 +525,39 @@
         else:
             res['status'] = ''
         if hasattr(vtodo, 'categories'):
-            category_ids = category_obj.search([
+            categories = Category.search([
                 ('name', 'in', [x for x in vtodo.categories.value]),
                 ])
-            categories = category_obj.browse(category_ids)
             category_names2ids = {}
             for category in categories:
                 category_names2ids[category.name] = category.id
             for category in vtodo.categories.value:
                 if category not in category_names2ids:
-                    category_ids.append(category_obj.create({
+                    categories.append(Category.create({
                         'name': category,
                         }))
-            res['categories'] = [('set', category_ids)]
+            res['categories'] = [('set', [c.id for c in categories])]
         else:
             res['categories'] = [('unlink_all',)]
         if hasattr(vtodo, 'class'):
             if getattr(vtodo, 'class').value.lower() in \
-                    dict(self.classification.selection):
+                    dict(cls.classification.selection):
                 res['classification'] = getattr(vtodo, 'class').value.lower()
             else:
                 res['classification'] = 'public'
         else:
             res['classification'] = 'public'
         if hasattr(vtodo, 'location'):
-            location_ids = location_obj.search([
+            locations = Location.search([
                 ('name', '=', vtodo.location.value),
                 ], limit=1)
-            if not location_ids:
-                location_id = location_obj.create({
+            if not locations:
+                location, = Location.create({
                     'name': vtodo.location.value,
                     })
             else:
-                location_id = location_ids[0]
-            res['location'] = location_id
+                location, = locations
+            res['location'] = location.id
         else:
             res['location'] = None
 
@@ -590,7 +579,7 @@
         if hasattr(vtodo, 'attendee'):
             while vtodo.attendee_list:
                 attendee = vtodo.attendee_list.pop()
-                vals = attendee_obj.attendee2values(attendee)
+                vals = Attendee.attendee2values(attendee)
                 if vals['email'] in attendees_todel:
                     res['attendees'].append(('write',
                         attendees_todel[vals['email']], vals))
@@ -606,7 +595,7 @@
             while vtodo.rdate_list:
                 rdate = vtodo.rdate_list.pop()
                 for date in rdate.value:
-                    vals = rdate_obj.date2values(date)
+                    vals = Rdate.date2values(date)
                     res['rdates'].append(('create', vals))
 
         res['exdates'] = []
@@ -616,7 +605,7 @@
             while vtodo.exdate_list:
                 exdate = vtodo.exdate_list.pop()
                 for date in exdate.value:
-                    vals = exdate_obj.date2values(date)
+                    vals = Exdate.date2values(date)
                     res['exdates'].append(('create', vals))
 
         res['rrules'] = []
@@ -625,7 +614,7 @@
         if hasattr(vtodo, 'rrule'):
             while vtodo.rrule_list:
                 rrule = vtodo.rrule_list.pop()
-                vals = rrule_obj.rule2values(rrule)
+                vals = Rrule.rule2values(rrule)
                 res['rrules'].append(('create', vals))
 
         res['exrules'] = []
@@ -634,7 +623,7 @@
         if hasattr(vtodo, 'exrule'):
             while vtodo.exrule_list:
                 exrule = vtodo.exrule_list.pop()
-                vals = exrule_obj.rule2values(exrule)
+                vals = Exrule.rule2values(exrule)
                 res['exrules'].append(('create', vals))
 
         if todo:
@@ -644,7 +633,7 @@
             res.setdefault('alarms', [])
             while vtodo.valarm_list:
                 valarm = vtodo.valarm_list.pop()
-                vals = alarm_obj.valarm2values(valarm)
+                vals = Alarm.valarm2values(valarm)
                 res['alarms'].append(('create', vals))
 
         if hasattr(ical, 'vtimezone'):
@@ -668,7 +657,7 @@
                             == vtodo.recurrence_id.value:
                         todo_id = occurence.id
                         occurences_todel.remove(occurence.id)
-            vals = self.ical2values(todo_id, ical, calendar_id, vtodo=vtodo)
+            vals = cls.ical2values(todo_id, ical, calendar_id, vtodo=vtodo)
             if todo:
                 vals['uuid'] = todo.uuid
             else:
@@ -683,29 +672,16 @@
             res['occurences'].append(('delete', occurences_todel))
         return res
 
-    def todo2ical(self, todo):
+    def todo2ical(self):
         '''
         Return an iCalendar instance of vobject for todo
-
-        :param todo: a BrowseRecord of calendar.todo
-            or a calendar.todo id
-        :return: an iCalendar instance of vobject
         '''
         pool = Pool()
-        user_obj = pool.get('res.user')
-        alarm_obj = pool.get('calendar.todo.alarm')
-        attendee_obj = pool.get('calendar.todo.attendee')
-        rdate_obj = pool.get('calendar.todo.rdate')
-        exdate_obj = pool.get('calendar.todo.exdate')
-        rrule_obj = pool.get('calendar.todo.rrule')
-        exrule_obj = pool.get('calendar.todo.exrule')
+        User = pool.get('res.user')
 
-        if isinstance(todo, (int, long)):
-            todo = self.browse(todo)
-
-        user = user_obj.browse(Transaction().user)
-        if todo.timezone:
-            tztodo = dateutil.tz.gettz(todo.timezone)
+        user = User(Transaction().user)
+        if self.timezone:
+            tztodo = dateutil.tz.gettz(self.timezone)
         elif user.timezone:
             tztodo = dateutil.tz.gettz(user.timezone)
         else:
@@ -713,331 +689,309 @@
 
         ical = vobject.iCalendar()
         vtodo = ical.add('vtodo')
-        if todo.vtodo:
-            ical.vtodo = vobject.readOne(str(todo.vtodo))
+        if self.vtodo:
+            ical.vtodo = vobject.readOne(str(self.vtodo))
             vtodo = ical.vtodo
             ical.vtodo.transformToNative()
-        if todo.summary:
+        if self.summary:
             if not hasattr(vtodo, 'summary'):
                 vtodo.add('summary')
-            vtodo.summary.value = todo.summary
+            vtodo.summary.value = self.summary
         elif hasattr(vtodo, 'summary'):
             del vtodo.summary
-        if todo.percent_complete:
+        if self.percent_complete:
             if not hasattr(vtodo, 'percent-complete'):
                 vtodo.add('percent-complete')
-            vtodo.percent_complete.value = str(todo.percent_complete)
+            vtodo.percent_complete.value = str(self.percent_complete)
         elif hasattr(vtodo, 'percent_complete'):
             del vtodo.percent_complete
-        if todo.description:
+        if self.description:
             if not hasattr(vtodo, 'description'):
                 vtodo.add('description')
-            vtodo.description.value = todo.description
+            vtodo.description.value = self.description
         elif hasattr(vtodo, 'description'):
             del vtodo.description
 
-        if todo.completed:
+        if self.completed:
             if not hasattr(vtodo, 'completed'):
                 vtodo.add('completed')
-            vtodo.completed.value = todo.completed.replace(tzinfo=tzlocal)\
+            vtodo.completed.value = self.completed.replace(tzinfo=tzlocal)\
                     .astimezone(tzutc)
         elif hasattr(vtodo, 'completed'):
             del vtodo.completed
 
-        if todo.dtstart:
+        if self.dtstart:
             if not hasattr(vtodo, 'dtstart'):
                 vtodo.add('dtstart')
-            vtodo.dtstart.value = todo.dtstart.replace(tzinfo=tzlocal)\
+            vtodo.dtstart.value = self.dtstart.replace(tzinfo=tzlocal)\
                     .astimezone(tztodo)
         elif hasattr(vtodo, 'dtstart'):
             del vtodo.dtstart
 
-        if todo.due:
+        if self.due:
             if not hasattr(vtodo, 'due'):
                 vtodo.add('due')
-            vtodo.due.value = todo.due.replace(tzinfo=tzlocal)\
+            vtodo.due.value = self.due.replace(tzinfo=tzlocal)\
                     .astimezone(tztodo)
         elif hasattr(vtodo, 'due'):
             del vtodo.due
 
         if not hasattr(vtodo, 'created'):
             vtodo.add('created')
-        vtodo.created.value = todo.create_date.replace(
+        vtodo.created.value = self.create_date.replace(
             tzinfo=tzlocal).astimezone(tztodo)
         if not hasattr(vtodo, 'dtstamp'):
             vtodo.add('dtstamp')
-        date = todo.write_date or todo.create_date
+        date = self.write_date or self.create_date
         vtodo.dtstamp.value = date.replace(tzinfo=tzlocal).astimezone(tztodo)
         if not hasattr(vtodo, 'last-modified'):
             vtodo.add('last-modified')
         vtodo.last_modified.value = date.replace(
             tzinfo=tzlocal).astimezone(tztodo)
-        if todo.recurrence and todo.parent:
+        if self.recurrence and self.parent:
             if not hasattr(vtodo, 'recurrence-id'):
                 vtodo.add('recurrence-id')
-            vtodo.recurrence_id.value = todo.recurrence\
+            vtodo.recurrence_id.value = self.recurrence\
                     .replace(tzinfo=tzlocal).astimezone(tztodo)
         elif hasattr(vtodo, 'recurrence-id'):
             del vtodo.recurrence_id
-        if todo.status:
+        if self.status:
             if not hasattr(vtodo, 'status'):
                 vtodo.add('status')
-            vtodo.status.value = todo.status.upper()
+            vtodo.status.value = self.status.upper()
         elif hasattr(vtodo, 'status'):
             del vtodo.status
         if not hasattr(vtodo, 'uid'):
             vtodo.add('uid')
-        vtodo.uid.value = todo.uuid
+        vtodo.uid.value = self.uuid
         if not hasattr(vtodo, 'sequence'):
             vtodo.add('sequence')
-        vtodo.sequence.value = str(todo.sequence) or '0'
-        if todo.categories:
+        vtodo.sequence.value = str(self.sequence) or '0'
+        if self.categories:
             if not hasattr(vtodo, 'categories'):
                 vtodo.add('categories')
-            vtodo.categories.value = [x.name for x in todo.categories]
+            vtodo.categories.value = [x.name for x in self.categories]
         elif hasattr(vtodo, 'categories'):
             del vtodo.categories
         if not hasattr(vtodo, 'class'):
             vtodo.add('class')
-            getattr(vtodo, 'class').value = todo.classification.upper()
+            getattr(vtodo, 'class').value = self.classification.upper()
         elif getattr(vtodo, 'class').value.lower() in \
                 dict(self.classification.selection):
-            getattr(vtodo, 'class').value = todo.classification.upper()
-        if todo.location:
+            getattr(vtodo, 'class').value = self.classification.upper()
+        if self.location:
             if not hasattr(vtodo, 'location'):
                 vtodo.add('location')
-            vtodo.location.value = todo.location.name
+            vtodo.location.value = self.location.name
         elif hasattr(vtodo, 'location'):
             del vtodo.location
 
-        if todo.organizer:
+        if self.organizer:
             if not hasattr(vtodo, 'organizer'):
                 vtodo.add('organizer')
-            vtodo.organizer.value = 'MAILTO:' + todo.organizer
+            vtodo.organizer.value = 'MAILTO:' + self.organizer
         elif hasattr(vtodo, 'organizer'):
             del vtodo.organizer
 
         vtodo.attendee_list = []
-        for attendee in todo.attendees:
-            vtodo.attendee_list.append(
-                attendee_obj.attendee2attendee(attendee))
+        for attendee in self.attendees:
+            vtodo.attendee_list.append(attendee.attendee2attendee())
 
-        if todo.rdates:
+        if self.rdates:
             vtodo.add('rdate')
             vtodo.rdate.value = []
-            for rdate in todo.rdates:
-                vtodo.rdate.value.append(rdate_obj.date2date(rdate))
+            for rdate in self.rdates:
+                vtodo.rdate.value.append(rdate.date2date())
 
-        if todo.exdates:
+        if self.exdates:
             vtodo.add('exdate')
             vtodo.exdate.value = []
-            for exdate in todo.exdates:
-                vtodo.exdate.value.append(exdate_obj.date2date(exdate))
+            for exdate in self.exdates:
+                vtodo.exdate.value.append(exdate.date2date())
 
-        if todo.rrules:
-            for rrule in todo.rrules:
-                vtodo.add('rrule').value = rrule_obj.rule2rule(rrule)
+        if self.rrules:
+            for rrule in self.rrules:
+                vtodo.add('rrule').value = rrule.rule2rule()
 
-        if todo.exrules:
-            for exrule in todo.exrules:
-                vtodo.add('exrule').value = exrule_obj.rule2rule(exrule)
+        if self.exrules:
+            for exrule in self.exrules:
+                vtodo.add('exrule').value = exrule.rule2rule()
 
         vtodo.valarm_list = []
-        for alarm in todo.alarms:
-            valarm = alarm_obj.alarm2valarm(alarm)
+        for alarm in self.alarms:
+            valarm = alarm.alarm2valarm()
             if valarm:
                 vtodo.valarm_list.append(valarm)
 
-        for occurence in todo.occurences:
+        for occurence in self.occurences:
             rical = self.todo2ical(occurence)
             ical.vtodo_list.append(rical.vtodo)
         return ical
 
-Todo()
-
 
 class TodoCategory(ModelSQL):
     'Todo - Category'
-    _description = __doc__
-    _name = 'calendar.todo-calendar.category'
-
+    __name__ = 'calendar.todo-calendar.category'
     todo = fields.Many2One('calendar.todo', 'To-Do', ondelete='CASCADE',
             required=True, select=True)
     category = fields.Many2One('calendar.category', 'Category',
             ondelete='CASCADE', required=True, select=True)
 
-TodoCategory()
-
 
 class TodoRDate(ModelSQL, ModelView):
     'Todo Recurrence Date'
-    _description = __doc__
-    _name = 'calendar.todo.rdate'
+    __name__ = 'calendar.todo.rdate'
     _inherits = {'calendar.date': 'calendar_date'}
     _rec_name = 'datetime'
-
     calendar_date = fields.Many2One('calendar.date', 'Calendar Date',
             required=True, ondelete='CASCADE', select=True)
     todo = fields.Many2One('calendar.todo', 'Todo', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.4: calendar_rdate renamed to calendar_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         old_column = 'calendar_rdate'
         if table.column_exist(old_column):
             table.column_rename(old_column, 'calendar_date')
 
-        return super(TodoRDate, self).init(module_name)
+        super(TodoRDate, cls).__register__(module_name)
 
-    def create(self, values):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def create(cls, values):
+        Todo = Pool().get('calendar.todo')
         if values.get('todo'):
             # Update write_date of todo
-            todo_obj.write(values['todo'], {})
-        return super(TodoRDate, self).create(values)
+            Todo.write([Todo(values['todo'])], {})
+        return super(TodoRDate, cls).create(values)
 
-    def write(self, ids, values):
-        todo_obj = Pool().get('calendar.todo')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        todo_ids = [x.todo.id for x in self.browse(ids)]
+    @classmethod
+    def write(cls, rdates, values):
+        Todo = Pool().get('calendar.todo')
+        todos = [x.todo for x in rdates]
         if values.get('todo'):
-            todo_ids.append(values['todo'])
-        if todo_ids:
+            todos.append(Todo(values['todo']))
+        if todos:
             # Update write_date of todo
-            todo_obj.write(todo_ids, {})
-        return super(TodoRDate, self).write(ids, values)
+            Todo.write(todos, {})
+        super(TodoRDate, cls).write(rdates, values)
 
-    def delete(self, ids):
-        todo_obj = Pool().get('calendar.todo')
-        rdate_obj = Pool().get('calendar.date')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        todo_rdates = self.browse(ids)
-        rdate_ids = [a.calendar_date.id for a in todo_rdates]
-        todo_ids = [x.todo.id for x in todo_rdates]
-        if todo_ids:
+    @classmethod
+    def delete(cls, todo_rdates):
+        pool = Pool()
+        Todo = pool.get('calendar.todo')
+        Rdate = pool.get('calendar.date')
+        rdates = [a.calendar_date for a in todo_rdates]
+        todos = [x.todo for x in todo_rdates]
+        if todos:
             # Update write_date of todo
-            todo_obj.write(todo_ids, {})
-        res = super(TodoRDate, self).delete(ids)
-        if rdate_ids:
-            rdate_obj.delete(rdate_ids)
-        return res
+            Todo.write(todos, {})
+        super(TodoRDate, cls).delete(todo_rdates)
+        if rdates:
+            Rdate.delete(rdates)
 
-    def _date2update(self, date):
-        date_obj = Pool().get('calendar.date')
-        return date_obj._date2update(date)
+    def _date2update(self):
+        return self.calendar_date._date2update()
 
-    def date2values(self, date):
-        date_obj = Pool().get('calendar.date')
-        return date_obj.date2values(date)
+    @classmethod
+    def date2values(cls, date):
+        Date = Pool().get('calendar.date')
+        return Date.date2values(date.calendar_date)
 
-    def date2date(self, date):
-        date_obj = Pool().get('calendar.date')
-        return date_obj.date2date(date)
-
-TodoRDate()
+    @classmethod
+    def date2date(cls, date):
+        Date = Pool().get('calendar.date')
+        return Date.date2date(date.calendar_date)
 
 
 class TodoRRule(ModelSQL, ModelView):
     'Recurrence Rule'
-    _description = __doc__
-    _name = 'calendar.todo.rrule'
+    __name__ = 'calendar.todo.rrule'
     _inherits = {'calendar.rrule': 'calendar_rrule'}
     _rec_name = 'freq'
-
     calendar_rrule = fields.Many2One('calendar.rrule', 'Calendar RRule',
             required=True, ondelete='CASCADE', select=True)
     todo = fields.Many2One('calendar.todo', 'Todo', ondelete='CASCADE',
             select=True, required=True)
 
-    def create(self, values):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def create(cls, values):
+        Todo = Pool().get('calendar.todo')
         if values.get('todo'):
             # Update write_date of todo
-            todo_obj.write(values['todo'], {})
-        return super(TodoRRule, self).create(values)
+            Todo.write([Todo(values['todo'])], {})
+        return super(TodoRRule, cls).create(values)
 
-    def write(self, ids, values):
-        todo_obj = Pool().get('calendar.todo')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        todo_ids = [x.todo.id for x in self.browse(ids)]
+    @classmethod
+    def write(cls, todo_rrules, values):
+        Todo = Pool().get('calendar.todo')
+        todos = [x.todo for x in todo_rrules]
         if values.get('todo'):
-            todo_ids.append(values['todo'])
-        if todo_ids:
+            todos.append(Todo(values['todo']))
+        if todos:
             # Update write_date of todo
-            todo_obj.write(todo_ids, {})
-        return super(TodoRRule, self).write(ids, values)
+            Todo.write(todos, {})
+        super(TodoRRule, cls).write(todo_rrules, values)
 
-    def delete(self, ids):
-        todo_obj = Pool().get('calendar.todo')
-        rrule_obj = Pool().get('calendar.rrule')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        todo_rrules = self.browse(ids)
-        rrule_ids = [a.calendar_rrule.id for a in todo_rrules]
-        todo_ids = [x.todo.id for x in todo_rrules]
-        if todo_ids:
+    @classmethod
+    def delete(cls, todo_rrules):
+        pool = Pool()
+        Todo = pool.get('calendar.todo')
+        Rrule = pool.get('calendar.rrule')
+        rrules = [a.calendar_rrule for a in todo_rrules]
+        todos = [x.todo for x in todo_rrules]
+        if todos:
             # Update write_date of todo
-            todo_obj.write(todo_ids, {})
-        res = super(TodoRRule, self).delete(ids)
-        if rrule_ids:
-            rrule_obj.delete(rrule_ids)
-        return res
+            Todo.write(todos, {})
+        super(TodoRRule, cls).delete(todo_rrules)
+        if rrules:
+            Rrule.delete(rrules)
 
-    def _rule2update(self, rule):
-        rule_obj = Pool().get('calendar.rrule')
-        return rule_obj._rule2update(rule)
+    def _rule2update(self):
+        return self.calendar_rrule._rule2update()
 
-    def rule2values(self, rule):
-        rule_obj = Pool().get('calendar.rrule')
-        return rule_obj.rule2values(rule)
+    @classmethod
+    def rule2values(cls, rule):
+        Rule = Pool().get('calendar.rrule')
+        return Rule.rule2values(rule.calendar_rrule)
 
-    def rule2rule(self, rule):
-        rule_obj = Pool().get('calendar.rrule')
-        return rule_obj.rule2rule(rule)
-
-TodoRRule()
+    @classmethod
+    def rule2rule(cls, rule):
+        Rule = Pool().get('calendar.rrule')
+        return Rule.rule2rule(rule.calendar_rrule)
 
 
 class TodoExDate(TodoRDate):
     'Exception Date'
-    _description = __doc__
-    _name = 'calendar.todo.exdate'
-
-TodoExDate()
+    __name__ = 'calendar.todo.exdate'
+    _table = 'calendar_todo_exdate'  # Needed to override TodoRDate._table
 
 
 class TodoExRule(TodoRRule):
     'Exception Rule'
-    _description = __doc__
-    _name = 'calendar.todo.exrule'
-
-TodoExRule()
+    __name__ = 'calendar.todo.exrule'
+    _table = 'calendar_todo_exrule'  # Needed to override TodoRRule._table
 
 
 class TodoAttendee(ModelSQL, ModelView):
     'Attendee'
-    _description = __doc__
-    _name = 'calendar.todo.attendee'
+    __name__ = 'calendar.todo.attendee'
     _inherits = {'calendar.attendee': 'calendar_attendee'}
-
     calendar_attendee = fields.Many2One('calendar.attendee',
         'Calendar Attendee', required=True, ondelete='CASCADE', select=True)
     todo = fields.Many2One('calendar.todo', 'Todo', ondelete='CASCADE',
             required=True, select=True)
 
-    def create(self, values):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def create(cls, values):
+        Todo = Pool().get('calendar.todo')
 
         if values.get('todo'):
             # Update write_date of todo
-            todo_obj.write(values['todo'], {})
-        res = super(TodoAttendee, self).create(values)
-        attendee = self.browse(res)
+            Todo.write([Todo(values['todo'])], {})
+        attendee = super(TodoAttendee, cls).create(values)
         todo = attendee.todo
         if (todo.calendar.owner
                 and (todo.organizer == todo.calendar.owner.email
@@ -1052,36 +1006,34 @@
                         if x.email != todo.parent.organizer]
             if attendee_emails:
                 with Transaction().set_user(0):
-                    todo_ids = todo_obj.search([
+                    todos = Todo.search([
                         ('uuid', '=', todo.uuid),
                         ('calendar.owner.email', 'in', attendee_emails),
                         ('id', '!=', todo.id),
                         ('recurrence', '=', todo.recurrence),
                         ])
-                    for todo_id in todo_ids:
-                        self.copy(res, default={
-                            'todo': todo_id,
+                    for todo in todos:
+                        cls.copy([attendee], default={
+                            'todo': todo.id,
                             })
-        return res
+        return attendee
 
-    def write(self, ids, values):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def write(cls, attendees, values):
+        Todo = Pool().get('calendar.todo')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        todo_ids = [x.todo.id for x in self.browse(ids)]
+        todos = [x.todo.id for x in attendees]
         if values.get('todo'):
-            todo_ids.append(values['todo'])
-        if todo_ids:
+            todos.append(Todo(values['todo']))
+        if todos:
             # Update write_date of todo
-            todo_obj.write(todo_ids, {})
+            Todo.write(todos, {})
 
         if 'email' in values:
             values = values.copy()
             del values['email']
 
-        res = super(TodoAttendee, self).write(ids, values)
-        attendees = self.browse(ids)
+        super(TodoAttendee, cls).write(attendees, values)
         for attendee in attendees:
             todo = attendee.todo
             if todo.calendar.owner \
@@ -1096,7 +1048,7 @@
                             if x.email != todo.parent.organizer]
                 if attendee_emails:
                     with Transaction().set_user(0):
-                        attendee_ids = self.search([
+                        attendees2 = cls.search([
                             ('todo.uuid', '=', todo.uuid),
                             ('todo.calendar.owner.email', 'in',
                                     attendee_emails),
@@ -1104,25 +1056,21 @@
                             ('todo.recurrence', '=', todo.recurrence),
                             ('email', '=', attendee.email),
                             ])
-                        self.write(attendee_ids, self._attendee2update(
-                                attendee))
-        return res
+                        cls.write(attendees2, attendee._attendee2update())
 
-    def delete(self, ids):
-        todo_obj = Pool().get('calendar.todo')
-        attendee_obj = Pool().get('calendar.attendee')
+    @classmethod
+    def delete(cls, todo_attendees):
+        pool = Pool()
+        Todo = pool.get('calendar.todo')
+        Attendee = pool.get('calendar.attendee')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        todo_attendees = self.browse(ids)
-        calendar_attendee_ids = [a.calendar_attendee.id \
-                for a in todo_attendees]
-        todo_ids = [x.todo.id for x in todo_attendees]
-        if todo_ids:
+        calendar_attendees = [a.calendar_attendee for a in todo_attendees]
+        todos = [x.todo for x in todo_attendees]
+        if todos:
             # Update write_date of todo
-            todo_obj.write(todo_ids, {})
+            Todo.write(todos, {})
 
-        for attendee in self.browse(ids):
+        for attendee in todo_attendees:
             todo = attendee.todo
             if todo.calendar.owner \
                     and (todo.organizer == todo.calendar.owner.email \
@@ -1136,7 +1084,7 @@
                             if x.email != todo.parent.organizer]
                 if attendee_emails:
                     with Transaction().set_user(0):
-                        attendee_ids = self.search([
+                        attendees = cls.search([
                             ('todo.uuid', '=', todo.uuid),
                             ('todo.calendar.owner.email', 'in',
                                 attendee_emails),
@@ -1144,7 +1092,7 @@
                             ('todo.recurrence', '=', todo.recurrence),
                             ('email', '=', attendee.email),
                             ])
-                        self.delete(attendee_ids)
+                        cls.delete(attendees)
             elif todo.calendar.organizer \
                     and ((todo.organizer \
                     or (todo.parent and todo.parent.organizer)) \
@@ -1154,110 +1102,98 @@
                 else:
                     organizer = todo.parent.organizer
                 with Transaction().set_user(0):
-                    attendee_ids = self.search([
+                    attendees = cls.search([
                         ('todo.uuid', '=', todo.uuid),
                         ('todo.calendar.owner.email', '=', organizer),
                         ('id', '!=', attendee.id),
                         ('todo.recurrence', '=', todo.recurrence),
                         ('email', '=', attendee.email),
                         ])
-                    if attendee_ids:
-                        self.write(attendee_ids, {
+                    if attendees:
+                        cls.write(attendees, {
                             'status': 'declined',
                             })
-        res = super(TodoAttendee, self).delete(ids)
-        if calendar_attendee_ids:
-            attendee_obj.delete(calendar_attendee_ids)
-        return res
+        super(TodoAttendee, cls).delete(todo_attendees)
+        if calendar_attendees:
+            Attendee.delete(calendar_attendees)
 
-    def copy(self, ids, default=None):
-        attendee_obj = Pool().get('calendar.attendee')
+    @classmethod
+    def copy(cls, todo_attendees, default=None):
+        Attendee = Pool().get('calendar.attendee')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for attendee in self.browse(ids):
-            default['calendar_attendee'] = attendee_obj.copy(
-                    attendee.calendar_attendee.id)
-            new_id = super(TodoAttendee, self).copy(attendee.id,
-                    default=default)
-            new_ids.append(new_id)
-        if int_id:
-            return new_ids[0]
-        return new_ids
+        new_attendees = []
+        for attendee in todo_attendees:
+            default['calendar_attendee'], = Attendee.copy(
+                [attendee.calendar_attendee])
+            new_attendee, = super(TodoAttendee, cls).copy([attendee],
+                default=default)
+            new_attendees.append(new_attendee)
+        return new_attendees
 
-    def _attendee2update(self, attendee):
-        attendee_obj = Pool().get('calendar.attendee')
-        return attendee_obj._attendee2update(attendee)
+    def _attendee2update(self):
+        return self.calendar_attendee._attendee2update()
 
-    def attendee2values(self, attendee):
-        attendee_obj = Pool().get('calendar.attendee')
-        return attendee_obj.attendee2values(attendee)
+    @classmethod
+    def attendee2values(cls, attendee):
+        Attendee = Pool().get('calendar.attendee')
+        return Attendee.attendee2values(attendee.calendar_attendee)
 
-    def attendee2attendee(self, attendee):
-        attendee_obj = Pool().get('calendar.attendee')
-        return attendee_obj.attendee2attendee(attendee)
-
-TodoAttendee()
+    @classmethod
+    def attendee2attendee(cls, attendee):
+        Attendee = Pool().get('calendar.attendee')
+        return Attendee.attendee2attendee(attendee.calendar_attendee)
 
 
 class TodoAlarm(ModelSQL):
     'Alarm'
-    _description = __doc__
-    _name = 'calendar.todo.alarm'
+    __name__ = 'calendar.todo.alarm'
     _inherits = {'calendar.alarm': 'calendar_alarm'}
-
     calendar_alarm = fields.Many2One('calendar.alarm', 'Calendar Alarm',
             required=True, ondelete='CASCADE', select=True)
     todo = fields.Many2One('calendar.todo', 'Todo', ondelete='CASCADE',
             required=True, select=True)
 
-    def create(self, values):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def create(cls, values):
+        Todo = Pool().get('calendar.todo')
         if values.get('todo'):
             # Update write_date of todo
-            todo_obj.write(values['todo'], {})
-        return super(TodoAlarm, self).create(values)
+            Todo.write([Todo(values['todo'])], {})
+        return super(TodoAlarm, cls).create(values)
 
-    def write(self, ids, values):
-        todo_obj = Pool().get('calendar.todo')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        todo_ids = [x.todo.id for x in self.browse(ids)]
+    @classmethod
+    def write(cls, alarms, values):
+        Todo = Pool().get('calendar.todo')
+        todos = [x.todo for x in alarms]
         if values.get('todo'):
-            todo_ids.append(values['todo'])
-        if todo_ids:
+            todos.append(Todo(values['todo']))
+        if todos:
             # Update write_date of todo
-            todo_obj.write(todo_ids, {})
-        return super(TodoAlarm, self).write(ids, values)
+            Todo.write(todos, {})
+        super(TodoAlarm, cls).write(alarms, values)
 
-    def delete(self, ids):
-        todo_obj = Pool().get('calendar.todo')
-        alarm_obj = Pool().get('calendar.alarm')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        todo_alarms = self.browse(ids)
-        alarm_ids = [a.calendar_alarm.id for a in todo_alarms]
-        todo_ids = [x.todo.id for x in todo_alarms]
-        if todo_ids:
+    @classmethod
+    def delete(cls, todo_alarms):
+        pool = Pool()
+        Todo = pool.get('calendar.todo')
+        Alarm = pool.get('calendar.alarm')
+        alarms = [a.calendar_alarm for a in todo_alarms]
+        todos = [x.todo for x in todo_alarms]
+        if todos:
             # Update write_date of todo
-            todo_obj.write(todo_ids, {})
-        res = super(TodoAlarm, self).delete(ids)
-        if alarm_ids:
-            alarm_obj.delete(alarm_ids)
-        return res
+            Todo.write(todos, {})
+        super(TodoAlarm, cls).delete(todo_alarms)
+        if alarms:
+            Alarm.delete(alarms)
 
-    def valarm2values(self, alarm):
-        alarm_obj = Pool().get('calendar.alarm')
-        return alarm_obj.valarm2values(alarm)
+    @classmethod
+    def valarm2values(cls, alarm):
+        Alarm = Pool().get('calendar.alarm')
+        return Alarm.valarm2values(alarm.calendar_alarm)
 
     def alarm2valarm(self, alarm):
-        alarm_obj = Pool().get('calendar.alarm')
-        return alarm_obj.alarm2valarm(alarm)
-
-TodoAlarm()
+        Alarm = Pool().get('calendar.alarm')
+        return Alarm.alarm2valarm(alarm.calendar_alarm)
diff -r 873d6d76778e trytond/modules/calendar_todo/webdav.py
--- a/trytond/modules/calendar_todo/webdav.py	Wed Sep 19 22:19:45 2012 +0200
+++ b/trytond/modules/calendar_todo/webdav.py	Mon Sep 24 20:36:40 2012 +0200
@@ -3,36 +3,33 @@
 import vobject
 import urllib
 from pywebdav.lib.errors import DAV_NotFound, DAV_Forbidden
-from trytond.model import ModelView, ModelSQL
 from trytond.tools import reduce_ids
 from trytond.transaction import Transaction
 from trytond.cache import Cache
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Collection']
+__metaclass__ = PoolMeta
 
-class Collection(ModelSQL, ModelView):
 
-    _name = "webdav.collection"
+class Collection:
+    __name__ = "webdav.collection"
+    _todo_cache = Cache('webdav_collection.todo')
 
-    @Cache('webdav_collection.todo')
-    def todo(self, uri, calendar_id=False):
+    @classmethod
+    def todo(cls, uri, calendar_id=False):
         '''
         Return the todo id in the uri
-
-        :param uri: the uri
-        :param calendar_id: the calendar id
-        :return: todo id
-            or None if there is no todo
         '''
-        todo_obj = Pool().get('calendar.todo')
+        Todo = Pool().get('calendar.todo')
 
         if uri and uri.startswith('Calendars/'):
             calendar, todo_uri = (uri[10:].split('/', 1) + [None])[0:2]
             if not calendar_id:
-                calendar_id = self.calendar(uri)
+                calendar_id = cls.calendar(uri)
                 if not calendar_id:
                     return None
-            todo_ids = todo_obj.search([
+            todo_ids = Todo.search([
                 ('calendar', '=', calendar_id),
                 ('uuid', '=', todo_uri[:-4]),
                 ('parent', '=', None),
@@ -40,12 +37,10 @@
             if todo_ids:
                 return todo_ids[0]
 
-    def _caldav_filter_domain_todo(self, filter):
+    @classmethod
+    def _caldav_filter_domain_todo(cls, filter):
         '''
         Return a domain for caldav filter on todo
-
-        :param filter: the DOM Element of filter
-        :return: a list for domain
         '''
         res = []
         if not filter:
@@ -98,78 +93,81 @@
                         continue
                     if uri:
                         uri = urllib.unquote_plus(uri)
-                    todo_id = self.todo(uri)
+                    todo_id = cls.todo(uri)
                     if todo_id:
                         ids.append(todo_id)
             return [('id', 'in', ids)]
         return res
 
-    def get_childs(self, uri, filter=None, cache=None):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def get_childs(cls, uri, filter=None, cache=None):
+        Todo = Pool().get('calendar.todo')
 
-        res = super(Collection, self).get_childs(uri, filter=filter,
+        res = super(Collection, cls).get_childs(uri, filter=filter,
                 cache=cache)
 
         if uri and (uri not in ('Calendars', 'Calendars/')) and \
                 uri.startswith('Calendars/'):
-            calendar_id = self.calendar(uri)
+            calendar_id = cls.calendar(uri)
             if  calendar_id and not (uri[10:].split('/', 1) + [None])[1]:
-                domain = self._caldav_filter_domain_todo(filter)
-                todo_ids = todo_obj.search([
+                domain = cls._caldav_filter_domain_todo(filter)
+                todos = Todo.search([
                     ('calendar', '=', calendar_id),
                     domain,
                     ])
-                todos = todo_obj.browse(todo_ids)
                 if cache is not None:
                     cache.setdefault('_calendar', {})
-                    cache['_calendar'].setdefault(todo_obj._name, {})
-                    for todo_id in todo_ids:
-                        cache['_calendar'][todo_obj._name][todo_id] = {}
+                    cache['_calendar'].setdefault(Todo.__name__, {})
+                    for todo in todos:
+                        cache['_calendar'][Todo.__name__][todo.id] = {}
                 return res + [x.uuid + '.ics' for x in todos]
 
         return res
 
-    def get_resourcetype(self, uri, cache=None):
+    @classmethod
+    def get_resourcetype(cls, uri, cache=None):
         from DAV.constants import COLLECTION, OBJECT
         if uri in ('Calendars', 'Calendars/'):
             return COLLECTION
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 return COLLECTION
-            if self.todo(uri, calendar_id=calendar_id):
+            if cls.todo(uri, calendar_id=calendar_id):
                 return OBJECT
-        elif self.calendar(uri, ics=True):
+        elif cls.calendar(uri, ics=True):
             return OBJECT
-        return super(Collection, self).get_resourcetype(uri, cache=cache)
+        return super(Collection, cls).get_resourcetype(uri, cache=cache)
 
-    def get_contenttype(self, uri, cache=None):
-        if self.todo(uri) \
-                or self.calendar(uri, ics=True):
+    @classmethod
+    def get_contenttype(cls, uri, cache=None):
+        if (cls.todo(uri)
+                or cls.calendar(uri, ics=True)):
             return 'text/calendar'
-        return super(Collection, self).get_contenttype(uri, cache=cache)
+        return super(Collection, cls).get_contenttype(uri, cache=cache)
 
-    def get_creationdate(self, uri, cache=None):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def get_creationdate(cls, uri, cache=None):
+        Todo = Pool().get('calendar.todo')
 
         cursor = Transaction().cursor
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if not calendar_id:
-            calendar_id = self.calendar(uri, ics=True)
+            calendar_id = cls.calendar(uri, ics=True)
         if calendar_id and (uri[10:].split('/', 1) + [None])[1]:
 
-            todo_id = self.todo(uri, calendar_id=calendar_id)
+            todo_id = cls.todo(uri, calendar_id=calendar_id)
             if todo_id:
                 if cache is not None:
                     cache.setdefault('_calendar', {})
-                    cache['_calendar'].setdefault(todo_obj._name, {})
-                    ids = cache['_calendar'][todo_obj._name].keys()
+                    cache['_calendar'].setdefault(Todo.__name__, {})
+                    ids = cache['_calendar'][Todo.__name__].keys()
                     if todo_id not in ids:
                         ids.append(todo_id)
                     elif 'creationdate' in cache['_calendar'][
-                            todo_obj._name][todo_id]:
-                        return cache['_calendar'][todo_obj._name][
+                            Todo.__name__][todo_id]:
+                        return cache['_calendar'][Todo.__name__][
                             todo_id]['creationdate']
                 else:
                     ids = [todo_id]
@@ -179,39 +177,40 @@
                     red_sql, red_ids = reduce_ids('id', sub_ids)
                     cursor.execute('SELECT id, ' \
                             'EXTRACT(epoch FROM create_date) ' \
-                        'FROM "' + todo_obj._table + '" ' \
+                        'FROM "' + Todo.__table__ + '" ' \
                         'WHERE ' + red_sql, red_ids)
                     for todo_id2, date in cursor.fetchall():
                         if todo_id2 == todo_id:
                             res = date
                         if cache is not None:
-                            cache['_calendar'][todo_obj._name]\
+                            cache['_calendar'][Todo.__name__]\
                                     .setdefault(todo_id2, {})
-                            cache['_calendar'][todo_obj._name][
+                            cache['_calendar'][Todo.__name__][
                                 todo_id2]['creationdate'] = date
                 if res is not None:
                     return res
 
-        return super(Collection, self).get_creationdate(uri, cache=cache)
+        return super(Collection, cls).get_creationdate(uri, cache=cache)
 
-    def get_lastmodified(self, uri, cache=None):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def get_lastmodified(cls, uri, cache=None):
+        Todo = Pool().get('calendar.todo')
 
         cursor = Transaction().cursor
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id and (uri[10:].split('/', 1) + [None])[1]:
-            todo_id = self.todo(uri, calendar_id=calendar_id)
+            todo_id = cls.todo(uri, calendar_id=calendar_id)
             if todo_id:
                 if cache is not None:
                     cache.setdefault('_calendar', {})
-                    cache['_calendar'].setdefault(todo_obj._name, {})
-                    ids = cache['_calendar'][todo_obj._name].keys()
+                    cache['_calendar'].setdefault(Todo.__name__, {})
+                    ids = cache['_calendar'][Todo.__name__].keys()
                     if todo_id not in ids:
                         ids.append(todo_id)
                     elif 'lastmodified' in cache['_calendar'][
-                            todo_obj._name][todo_id]:
-                        return cache['_calendar'][todo_obj._name][
+                            Todo.__name__][todo_id]:
+                        return cache['_calendar'][Todo.__name__][
                             todo_id]['lastmodified']
                 else:
                     ids = [todo_id]
@@ -224,7 +223,7 @@
                     cursor.execute('SELECT COALESCE(parent, id), ' \
                                 'MAX(EXTRACT(epoch FROM ' \
                                 'COALESCE(write_date, create_date))) ' \
-                            'FROM "' + todo_obj._table + '" ' \
+                            'FROM "' + Todo.__table__ + '" ' \
                             'WHERE ' + red_id_sql + ' ' \
                                 'OR ' + red_parent_sql + ' ' \
                             'GROUP BY parent, id', red_id_ids + red_parent_ids)
@@ -232,83 +231,85 @@
                         if todo_id2 == todo_id:
                             res = date
                         if cache is not None:
-                            cache['_calendar'][todo_obj._name]\
+                            cache['_calendar'][Todo.__name__]\
                                     .setdefault(todo_id2, {})
-                            cache['_calendar'][todo_obj._name][
+                            cache['_calendar'][Todo.__name__][
                                 todo_id2]['lastmodified'] = date
                 if res is not None:
                     return res
 
-        return super(Collection, self).get_lastmodified(uri, cache=cache)
+        return super(Collection, cls).get_lastmodified(uri, cache=cache)
 
-    def get_data(self, uri, cache=None):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def get_data(cls, uri, cache=None):
+        Todo = Pool().get('calendar.todo')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 raise DAV_NotFound
-            todo_id = self.todo(uri, calendar_id=calendar_id)
+            todo_id = cls.todo(uri, calendar_id=calendar_id)
             if not todo_id:
-                return super(Collection, self).get_data(uri, cache=cache)
-            ical = todo_obj.todo2ical(todo_id)
+                return super(Collection, cls).get_data(uri, cache=cache)
+            ical = Todo(todo_id).todo2ical()
             return ical.serialize()
 
-        return super(Collection, self).get_data(uri, cache=cache)
+        return super(Collection, cls).get_data(uri, cache=cache)
 
-    def put(self, uri, data, content_type, cache=None):
-        todo_obj = Pool().get('calendar.todo')
-        calendar_obj = Pool().get('calendar.calendar')
+    @classmethod
+    def put(cls, uri, data, content_type, cache=None):
+        pool = Pool()
+        Todo = pool.get('calendar.todo')
+        Calendar = pool.get('calendar.calendar')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 raise DAV_Forbidden
-            todo_id = self.todo(uri, calendar_id=calendar_id)
+            todo_id = cls.todo(uri, calendar_id=calendar_id)
             ical = vobject.readOne(data)
             if not hasattr(ical, 'vtodo'):
-                return super(Collection, self).put(uri, data, content_type)
+                return super(Collection, cls).put(uri, data, content_type)
 
             if not todo_id:
 
-                values = todo_obj.ical2values(None, ical, calendar_id)
-                todo_id = todo_obj.create(values)
-                todo = todo_obj.browse(todo_id)
-                calendar = calendar_obj.browse(calendar_id)
+                values = Todo.ical2values(None, ical, calendar_id)
+                todo = Todo.create(values)
+                calendar = Calendar(calendar_id)
                 return Transaction().cursor.database_name + '/Calendars/' + \
                         calendar.name + '/' + todo.uuid + '.ics'
             else:
-                values = todo_obj.ical2values(todo_id, ical, calendar_id)
-                todo_obj.write(todo_id, values)
+                values = Todo.ical2values(todo_id, ical, calendar_id)
+                Todo.write([Todo(todo_id)], values)
                 return
 
-        return super(Collection, self).put(uri, data, content_type)
+        return super(Collection, cls).put(uri, data, content_type)
 
-    def rm(self, uri, cache=None):
-        todo_obj = Pool().get('calendar.todo')
+    @classmethod
+    def rm(cls, uri, cache=None):
+        Todo = Pool().get('calendar.todo')
 
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 raise DAV_Forbidden
-            todo_id = self.todo(uri, calendar_id=calendar_id)
+            todo_id = cls.todo(uri, calendar_id=calendar_id)
             if todo_id:
                 try:
-                    todo_obj.delete(todo_id)
+                    Todo.delete([Todo(todo_id)])
                 except Exception:
                     raise DAV_Forbidden
                 return 200
-        return super(Collection, self).rm(uri, cache=cache)
+        return super(Collection, cls).rm(uri, cache=cache)
 
-    def exists(self, uri, cache=None):
+    @classmethod
+    def exists(cls, uri, cache=None):
         if uri in ('Calendars', 'Calendars/'):
             return 1
-        calendar_id = self.calendar(uri)
+        calendar_id = cls.calendar(uri)
         if calendar_id:
             if not (uri[10:].split('/', 1) + [None])[1]:
                 return 1
-            if self.todo(uri, calendar_id=calendar_id):
+            if cls.todo(uri, calendar_id=calendar_id):
                 return 1
-        return super(Collection, self).exists(uri, cache=cache)
-
-Collection()
+        return super(Collection, cls).exists(uri, cache=cache)
diff -r e34b5d9c283b trytond/modules/carrier/__init__.py
--- a/trytond/modules/carrier/__init__.py	Fri Jun 08 19:33:51 2012 +0200
+++ b/trytond/modules/carrier/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,4 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .carrier import *
+
+
+def register():
+    Pool.register(
+        Carrier,
+        module='carrier', type_='model')
diff -r e34b5d9c283b trytond/modules/carrier/carrier.py
--- a/trytond/modules/carrier/carrier.py	Fri Jun 08 19:33:51 2012 +0200
+++ b/trytond/modules/carrier/carrier.py	Mon Sep 24 20:36:40 2012 +0200
@@ -4,12 +4,13 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Carrier']
+
 
 class Carrier(ModelSQL, ModelView):
     'Carrier'
-    _name = 'carrier'
+    __name__ = 'carrier'
     _inherits = {'party.party': 'party'}
-
     party = fields.Many2One('party.party', 'Party', required=True,
             ondelete='CASCADE')
     carrier_product = fields.Many2One('product.product', 'Carrier Product',
@@ -21,52 +22,41 @@
         ], 'Carrier Cost Method', required=True,
         help='Method to compute carrier cost')
 
-    def default_carrier_cost_method(self):
+    @staticmethod
+    def default_carrier_cost_method():
         return 'product'
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        names = {}
-        for carrier in self.browse(ids):
-            names[carrier.id] = '%s - %s' % (carrier.party.rec_name,
-                carrier.carrier_product.rec_name)
-        return names
+    def get_rec_name(self, name):
+        return '%s - %s' % (self.party.rec_name, self.carrier_product.rec_name)
 
-    def copy(self, ids, default=None):
-        party_obj = Pool().get('party.party')
+    @classmethod
+    def copy(self, carriers, default=None):
+        Party = Pool().get('party.party')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for carrier in self.browse(ids):
-            default['party'] = party_obj.copy(carrier.party.id)
-            new_id = super(Carrier, self).copy(carrier.id, default=default)
-            new_ids.append(new_id)
-        if int_id:
-            return new_ids[0]
-        return new_ids
+        new_carriers = []
+        for carrier in carriers:
+            party, = Party.copy([carrier.party])
+            default['party'] = party.id
+            new_carriers.extend(super(Carrier, self).copy([carrier],
+                    default=default))
+        return new_carriers
 
-    def get_sale_price(self, carrier):
+    def get_sale_price(self):
         'Compute carrier sale price with currency'
-        user_obj = Pool().get('res.user')
-        if carrier.carrier_cost_method == 'product':
-            user = user_obj.browse(Transaction().user
+        User = Pool().get('res.user')
+        if self.carrier_cost_method == 'product':
+            user = User(Transaction().user
                 or Transaction().context.get('user'))
-            return carrier.carrier_product.list_price, user.company.currency.id
+            return self.carrier_product.list_price, user.company.currency.id
         return 0, None
 
-    def get_purchase_price(self, carrier):
+    def get_purchase_price(self):
         'Compute carrier purchase price with currency'
-        user_obj = Pool().get('res.user')
-        if carrier.carrier_cost_method == 'product':
-            user = user_obj.browse(Transaction().user)
-            return carrier.carrier_product.cost_price, user.company.currency.id
+        User = Pool().get('res.user')
+        if self.carrier_cost_method == 'product':
+            user = User(Transaction().user)
+            return self.carrier_product.cost_price, user.company.currency.id
         return 0, None
-
-Carrier()
diff -r 1f2c537229ab trytond/modules/carrier_percentage/__init__.py
--- a/trytond/modules/carrier_percentage/__init__.py	Tue Sep 11 15:29:50 2012 +0200
+++ b/trytond/modules/carrier_percentage/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,6 +1,16 @@
 #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.pool import Pool
 from carrier import *
 from stock import *
 from sale import *
+
+
+def register():
+    Pool.register(
+        Carrier,
+        ShipmentIn,
+        ShipmentOut,
+        Sale,
+        module='carrier_percentage', type_='model')
diff -r 1f2c537229ab trytond/modules/carrier_percentage/carrier.py
--- a/trytond/modules/carrier_percentage/carrier.py	Tue Sep 11 15:29:50 2012 +0200
+++ b/trytond/modules/carrier_percentage/carrier.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,56 +1,52 @@
 #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 copy
 from decimal import Decimal
-from trytond.model import Model, fields
+from trytond.model import fields
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.pyson import Eval
 
+__all__ = ['Carrier']
+__metaclass__ = PoolMeta
 
-class Carrier(Model):
-    _name = 'carrier'
 
+class Carrier:
+    __name__ = 'carrier'
     percentage = fields.Numeric('Percentage', digits=(16, 8),
         states={
             'invisible': Eval('carrier_cost_method') != 'percentage',
             },
         depends=['carrier_cost_method'])
 
-    def __init__(self):
-        super(Carrier, self).__init__()
-        self.carrier_cost_method = copy.copy(self.carrier_cost_method)
-        self.carrier_cost_method.selection = \
-            self.carrier_cost_method.selection[:]
+    @classmethod
+    def __setup__(cls):
+        super(Carrier, cls).__setup__()
         selection = ('percentage', 'Percentage')
-        if selection not in self.carrier_cost_method.selection:
-            self.carrier_cost_method.selection.append(selection)
-        self._reset_columns()
+        if selection not in cls.carrier_cost_method.selection:
+            cls.carrier_cost_method.selection.append(selection)
 
-    def compute_percentage(self, carrier, amount, currency_id):
+    def compute_percentage(self, amount, currency_id):
         "Compute price based on a percentage of amount"
-        currency_obj = Pool().get('currency.currency')
+        Currency = Pool().get('currency.currency')
 
-        price = amount * carrier.percentage / Decimal(100)
+        price = amount * self.percentage / Decimal(100)
         if not currency_id:
             return price, currency_id
-        currency = currency_obj.browse(currency_id)
-        return currency_obj.round(currency, price), currency_id
+        currency = Currency(currency_id)
+        return currency.round(price), currency_id
 
-    def get_sale_price(self, carrier):
-        price, currency_id = super(Carrier, self).get_sale_price(carrier)
-        if carrier.carrier_cost_method == 'percentage':
+    def get_sale_price(self):
+        price, currency_id = super(Carrier, self).get_sale_price()
+        if self.carrier_cost_method == 'percentage':
             amount = Transaction().context.get('amount', Decimal(0))
             currency_id = Transaction().context.get('currency', currency_id)
-            return self.compute_percentage(carrier, amount, currency_id)
+            return self.compute_percentage(amount, currency_id)
         return price, currency_id
 
-    def get_purchase_price(self, carrier):
-        price, currency_id = super(Carrier, self).get_purchase_price(carrier)
-        if carrier.carrier_cost_method == 'percentage':
+    def get_purchase_price(self):
+        price, currency_id = super(Carrier, self).get_purchase_price()
+        if self.carrier_cost_method == 'percentage':
             amount = Transaction().context.get('amount', Decimal(0))
             currency_id = Transaction().context.get('currency', currency_id)
-            return self.compute_percentage(carrier, amount, currency_id)
+            return self.compute_percentage(amount, currency_id)
         return price, currency_id
-
-Carrier()
diff -r 1f2c537229ab trytond/modules/carrier_percentage/sale.py
--- a/trytond/modules/carrier_percentage/sale.py	Tue Sep 11 15:29:50 2012 +0200
+++ b/trytond/modules/carrier_percentage/sale.py	Mon Sep 24 20:36:40 2012 +0200
@@ -2,33 +2,28 @@
 #this repository contains the full copyright notices and license terms.
 from decimal import Decimal
 
-from trytond.model import Model
-from trytond.pool import Pool
+from trytond.pool import PoolMeta
 
+__all__ = ['Sale']
+__metaclass__ = PoolMeta
 
-class Sale(Model):
-    _name = 'sale.sale'
 
-    def _get_carrier_context(self, values):
-        pool = Pool()
-        carrier_obj = pool.get('carrier')
+class Sale:
+    __name__ = 'sale.sale'
 
-        context = super(Sale, self)._get_carrier_context(values)
+    def _get_carrier_context(self):
+        context = super(Sale, self)._get_carrier_context()
 
-        carrier = carrier_obj.browse(values['carrier'])
-        if carrier.carrier_cost_method != 'percentage':
+        if self.carrier.carrier_cost_method != 'percentage':
             return context
-        if not values.get('currency'):
+        if not self.currency:
             return context
         context = context.copy()
         amount = 0
-        for line in values.get('lines') or []:
-            if (line.get('unit_price') and line.get('quantity')
-                    and not line.get('shipment_cost')):
-                amount += (line['unit_price']
-                    * Decimal(str(line['quantity'] or 0)))
+        for line in self.lines or []:
+            if line.unit_price and line.quantity and not line.shipment_cost:
+                amount += (line.unit_price
+                    * Decimal(str(line.quantity or 0)))
         context['amount'] = amount
-        context['currency'] = values['currency']
+        context['currency'] = self.currency.id
         return context
-
-Sale()
diff -r 1f2c537229ab trytond/modules/carrier_percentage/stock.py
--- a/trytond/modules/carrier_percentage/stock.py	Tue Sep 11 15:29:50 2012 +0200
+++ b/trytond/modules/carrier_percentage/stock.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,105 +1,64 @@
 #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 itertools import izip
 
-from trytond.model import Model
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 
+__all__ = ['ShipmentIn', 'ShipmentOut']
+__metaclass__ = PoolMeta
+
 
 def _percentage_amount(lines, company):
     pool = Pool()
-    move_obj = pool.get('stock.move')
-    currency_obj = pool.get('currency.currency')
+    Move = pool.get('stock.move')
+    Currency = pool.get('currency.currency')
 
     amount = 0
     for line in lines or []:
-        if line['id'] >= 0:
-            move = move_obj.browse(line['id'])
-        else:
-            move = None
-        if 'unit_price' in line:
-            unit_price = line['unit_price']
-        elif move:
-            unit_price = move.unit_price
-        elif hasattr(move_obj, 'default_unit_price'):
-            unit_price = move_obj.default_unit_price()
-        else:
-            unit_price = Decimal('0')
-        if 'currency' in line:
-            currency_id = line['currency']
-        elif move:
-            currency_id = move.currency.id
-        elif hasattr(move_obj, 'default_currency'):
-            currency_id = move_obj.default_currency()
-        else:
-            currency_id = None
-        if currency_id:
-            unit_price = currency_obj.compute(currency_id, unit_price,
-                company.currency.id, round=False)
-        amount += unit_price * Decimal(str(line['quantity'] or 0))
+        unit_price = getattr(line, 'unit_price',
+            Move.default_unit_price() if hasattr(Move, 'default_unit_price')
+            else Decimal(0))
+        currency = getattr(line, 'currency',
+            Move.default_currency() if hasattr(Move, 'default_currency')
+            else None)
+        if currency:
+            unit_price = Currency.compute(currency, unit_price,
+                company.currency, round=False)
+        amount += unit_price * Decimal(str(line.quantity or 0))
     return amount
 
 
-class ShipmentIn(Model):
-    _name = 'stock.shipment.in'
+class ShipmentIn:
+    __name__ = 'stock.shipment.in'
 
-    def _get_carrier_context(self, values):
-        pool = Pool()
-        carrier_obj = pool.get('carrier')
-        company_obj = pool.get('company.company')
-        context = super(ShipmentIn, self)._get_carrier_context(values)
-        if not values.get('carrier'):
+    def _get_carrier_context(self):
+        Company = Pool().get('company.company')
+        context = super(ShipmentIn, self)._get_carrier_context()
+        if not self.carrier:
             return context
         context = context.copy()
-        carrier = carrier_obj.browse(values['carrier'])
-        if carrier.carrier_cost_method != 'percentage':
+        if self.carrier.carrier_cost_method != 'percentage':
             return context
-        company = company_obj.browse(Transaction().context['company'])
-        context['amount'] = _percentage_amount(values['incoming_moves'],
-            company)
+        company = Company(Transaction().context['company'])
+        context['amount'] = _percentage_amount(self.incoming_moves, company)
         context['currency'] = company.currency.id
         return context
 
-ShipmentIn()
 
+class ShipmentOut:
+    __name__ = 'stock.shipment.out'
 
-class ShipmentOut(Model):
-    _name = 'stock.shipment.out'
+    def _get_carrier_context(self):
+        Company = Pool().get('company.company')
 
-    def _get_carrier_context(self, values):
-        pool = Pool()
-        carrier_obj = pool.get('carrier')
-        company_obj = pool.get('company.company')
-
-        context = super(ShipmentOut, self)._get_carrier_context(values)
-        if not values.get('carrier'):
+        context = super(ShipmentOut, self)._get_carrier_context()
+        if not self.carrier:
             return context
         context = context.copy()
-        carrier = carrier_obj.browse(values['carrier'])
-        if carrier.carrier_cost_method != 'percentage':
+        if self.carrier.carrier_cost_method != 'percentage':
             return context
-        company = company_obj.browse(Transaction().context['company'])
-        context['amount'] = _percentage_amount(values['inventory_moves'],
-            company)
+        company = Company(Transaction().context['company'])
+        context['amount'] = _percentage_amount(self.inventory_moves, company)
         context['currency'] = company.currency.id
         return context
-
-    def get_carrier_context(self, shipment, values=None):
-        if values is None:
-            values = {}
-        values = values.copy()
-        inventory_moves = [{
-                'id': m.id,
-                'unit_price': m.unit_price,
-                'currency': m.currency.id,
-                'quantity': m.quantity,
-                } for m in shipment.inventory_moves]
-        values.setdefault('inventory_moves', inventory_moves)
-        for new, old in izip(inventory_moves, values['inventory_moves']):
-            old.update(new)
-        return super(ShipmentOut, self).get_carrier_context(shipment,
-            values=values)
-
-ShipmentOut()
diff -r 1f2c537229ab trytond/modules/carrier_percentage/tests/test_carrier_percentage.py
--- a/trytond/modules/carrier_percentage/tests/test_carrier_percentage.py	Tue Sep 11 15:29:50 2012 +0200
+++ b/trytond/modules/carrier_percentage/tests/test_carrier_percentage.py	Mon Sep 24 20:36:40 2012 +0200
@@ -49,42 +49,40 @@
         Test compute_percentage.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            party_id = self.party.create({
+            party = self.party.create({
                     'name': 'Carrier',
                     })
-            uom_id, = self.uom.search([
+            uom, = self.uom.search([
                     ('name', '=', 'Unit'),
                     ])
-            category_id = self.category.create({
+            category = self.category.create({
                     'name': 'Category',
                     })
-            product_id = self.product.create({
+            product = self.product.create({
                     'name': 'Carrier',
-                    'default_uom': uom_id,
-                    'category': category_id,
+                    'default_uom': uom.id,
+                    'category': category.id,
                     'type': 'service',
                     'list_price': Decimal(0),
                     'cost_price': Decimal(0),
                     })
-            currency_id, = self.currency.search([
+            currency, = self.currency.search([
                     ('code', '=', 'cu1'),
                     ])
-            carrier_id = self.carrier.create({
-                    'party': party_id,
-                    'carrier_product': product_id,
+            carrier = self.carrier.create({
+                    'party': party.id,
+                    'carrier_product': product.id,
                     'carrier_cost_method': 'percentage',
                     'percentage': Decimal(15),
                     })
-            carrier = self.carrier.browse(carrier_id)
             for amount, price in [
                     (Decimal(0), Decimal(0)),
                     (Decimal(100), Decimal('15.00')),
                     (Decimal(150), Decimal('22.50')),
                     ]:
                 self.assertEqual(
-                    self.carrier.compute_percentage(carrier, amount,
-                        currency_id),
-                    (price, currency_id))
+                    carrier.compute_percentage(amount, currency.id),
+                    (price, currency.id))
 
 
 def doctest_dropdb(test):
diff -r b7bad438669f trytond/modules/carrier_weight/__init__.py
--- a/trytond/modules/carrier_weight/__init__.py	Thu Sep 13 12:41:15 2012 +0200
+++ b/trytond/modules/carrier_weight/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,6 +1,17 @@
 #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.pool import Pool
 from carrier import *
 from stock import *
 from sale import *
+
+
+def register():
+    Pool.register(
+        Carrier,
+        WeightPriceList,
+        ShipmentIn,
+        ShipmentOut,
+        Sale,
+        module='carrier_weight', type_='model')
diff -r b7bad438669f trytond/modules/carrier_weight/carrier.py
--- a/trytond/modules/carrier_weight/carrier.py	Thu Sep 13 12:41:15 2012 +0200
+++ b/trytond/modules/carrier_weight/carrier.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,17 +1,18 @@
 #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 copy
 from decimal import Decimal
 
-from trytond.model import Model, ModelSQL, ModelView, fields
+from trytond.model import ModelSQL, ModelView, fields
 from trytond.pyson import Eval, Bool, Id
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 
+__all__ = ['Carrier', 'WeightPriceList']
+__metaclass__ = PoolMeta
 
-class Carrier(Model):
-    _name = 'carrier'
 
+class Carrier:
+    __name__ = 'carrier'
     weight_uom = fields.Many2One('product.uom', 'Weight Uom',
         domain=[('category', '=', Id('product', 'uom_cat_weight'))],
         states={
@@ -21,7 +22,7 @@
             },
         depends=['carrier_cost_method', 'weight_price_list'])
     weight_uom_digits = fields.Function(fields.Integer('Weight Uom Digits',
-            on_change_with=['weight_uom']), 'get_weight_uom_digits')
+            on_change_with=['weight_uom']), 'on_change_with_weight_uom_digits')
     weight_currency = fields.Many2One('currency.currency', 'Currency',
         states={
             'invisible': Eval('carrier_cost_method') != 'weight',
@@ -31,7 +32,7 @@
         depends=['carrier_cost_method', 'weight_price_list'])
     weight_currency_digits = fields.Function(fields.Integer(
             'Weight Currency Digits', on_change_with=['weight_currency']),
-        'get_weight_currency_digits')
+        'on_change_with_weight_currency_digits')
     weight_price_list = fields.One2Many('carrier.weight_price_list', 'carrier',
         'Price List',
         states={
@@ -40,98 +41,71 @@
             },
         depends=['carrier_cost_method', 'weight_uom', 'weight_currency'])
 
-    def __init__(self):
-        super(Carrier, self).__init__()
-        self.carrier_cost_method = copy.copy(self.carrier_cost_method)
-        self.carrier_cost_method.selection = \
-            self.carrier_cost_method.selection[:]
+    @classmethod
+    def __setup__(cls):
+        super(Carrier, cls).__setup__()
         selection = ('weight', 'Weight')
-        if selection not in self.carrier_cost_method.selection:
-            self.carrier_cost_method.selection.append(selection)
-        self._reset_columns()
+        if selection not in cls.carrier_cost_method.selection:
+            cls.carrier_cost_method.selection.append(selection)
 
-    def default_weight_uom_digits(self):
+    @staticmethod
+    def default_weight_uom_digits():
         return 2
 
-    def default_weight_currency_digits(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_weight_currency_digits():
+        Company = Pool().get('company.company')
         company = Transaction().context.get('company')
         if company:
-            return company_obj.browse(company).currency.digits
+            return Company(company).currency.digits
         return 2
 
-    def on_change_with_weight_uom_digits(self, values):
-        uom_obj = Pool().get('product.uom')
-        if values.get('weight_uom'):
-            uom = uom_obj.browse(values['weight_uom'])
-            return uom.digits
+    def on_change_with_weight_uom_digits(self, name=None):
+        if self.weight_uom:
+            return self.weight_uom.digits
         return 2
 
-    def get_weight_uom_digits(self, ids, name):
-        digits = {}
-        for carrier in self.browse(ids):
-            if carrier.weight_uom:
-                digits[carrier.id] = carrier.weight_uom.digits
-            else:
-                digits[carrier.id] = 2
-        return digits
-
-    def on_change_with_weight_currency_digits(self, values):
-        currency_obj = Pool().get('currency.currency')
-        if values.get('weight_currency'):
-            currency = currency_obj.browse(values['weight_currency'])
-            return currency.digits
+    def on_change_with_weight_currency_digits(self, name=None):
+        if self.weight_currency:
+            return self.weight_currency.digits
         return 2
 
-    def get_weight_currency_digits(self, ids, name):
-        digits = {}
-        for carrier in self.browse(ids):
-            if carrier.weight_currency:
-                digits[carrier.id] = carrier.weight_currency.digits
-            else:
-                digits[carrier.id] = 2
-        return digits
-
-    def compute_weight_price(self, carrier, weight):
+    def compute_weight_price(self, weight):
         "Compute price based on weight"
-        for line in reversed(carrier.weight_price_list):
+        for line in reversed(self.weight_price_list):
             if line.weight < weight:
                 return line.price
         return Decimal(0)
 
-    def get_sale_price(self, carrier):
-        price, currency_id = super(Carrier, self).get_sale_price(carrier)
-        if carrier.carrier_cost_method == 'weight':
+    def get_sale_price(self):
+        price, currency_id = super(Carrier, self).get_sale_price()
+        if self.carrier_cost_method == 'weight':
             weight_price = Decimal(0)
             for weight in Transaction().context.get('weights', []):
-                weight_price += self.compute_weight_price(carrier, weight)
-            return weight_price, carrier.weight_currency.id
+                weight_price += self.compute_weight_price(weight)
+            return weight_price, self.weight_currency.id
         return price, currency_id
 
-    def get_purchase_price(self, carrier):
-        price, currency_id = super(Carrier, self).get_purchase_price(carrier)
-        if carrier.carrier_cost_method == 'weight':
+    def get_purchase_price(self):
+        price, currency_id = super(Carrier, self).get_purchase_price()
+        if self.carrier_cost_method == 'weight':
             weight_price = Decimal(0)
             for weight in Transaction().context.get('weights', []):
-                weight_price += self.compute_weight_price(carrier, weight)
-            return weight_price, carrier.weight_currency.id
+                weight_price += self.compute_weight_price(weight)
+            return weight_price, self.weight_currency.id
         return price, currency_id
 
-Carrier()
-
 
 class WeightPriceList(ModelSQL, ModelView):
     'Carrier Weight Price List'
-    _name = 'carrier.weight_price_list'
-
+    __name__ = 'carrier.weight_price_list'
     carrier = fields.Many2One('carrier', 'Carrier', required=True, select=True)
     weight = fields.Float('Weight',
         digits=(16, Eval('_parent_carrier.weight_uom_digits', 2)))
     price = fields.Numeric('Price',
         digits=(16, Eval('_parent_carrier.weight_currency_digits', 2)))
 
-    def __init__(self):
-        super(WeightPriceList, self).__init__()
-        self._order.insert(0, ('weight', 'ASC'))
-
-WeightPriceList()
+    @classmethod
+    def __setup__(cls):
+        super(WeightPriceList, cls).__setup__()
+        cls._order.insert(0, ('weight', 'ASC'))
diff -r b7bad438669f trytond/modules/carrier_weight/sale.py
--- a/trytond/modules/carrier_weight/sale.py	Thu Sep 13 12:41:15 2012 +0200
+++ b/trytond/modules/carrier_weight/sale.py	Mon Sep 24 20:36:40 2012 +0200
@@ -3,12 +3,14 @@
 from functools import partial
 from itertools import groupby
 
-from trytond.model import Model
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Sale']
+__metaclass__ = PoolMeta
 
-class Sale(Model):
-    _name = 'sale.sale'
+
+class Sale:
+    __name__ = 'sale.sale'
 
     def _group_parcel_key(self, lines, line):
         """
@@ -16,45 +18,30 @@
         """
         return None
 
-    def _get_carrier_context(self, values):
-        pool = Pool()
-        carrier_obj = pool.get('carrier')
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
+    def _get_carrier_context(self):
+        Uom = Pool().get('product.uom')
 
-        context = super(Sale, self)._get_carrier_context(values)
+        context = super(Sale, self)._get_carrier_context()
 
-        carrier = carrier_obj.browse(values['carrier'])
-        if carrier.carrier_cost_method != 'weight':
+        if self.carrier.carrier_cost_method != 'weight':
             return context
         context = context.copy()
         weights = []
         context['weights'] = weights
-        product_ids = [line['product'] for line in values.get('lines') or []
-            if line.get('product')]
-        id2product = dict((p.id, p) for p in product_obj.browse(product_ids))
-        unit_ids = [line['unit'] for line in values.get('lines') or []
-            if line.get('unit')]
-        id2unit = dict((u.id, u) for u in uom_obj.browse(unit_ids))
 
-        lines = values.get('lines') or []
+        lines = self.lines or []
         keyfunc = partial(self._group_parcel_key, lines)
         lines = sorted(lines, key=keyfunc)
 
         for key, parcel in groupby(lines, key=keyfunc):
             weight = 0
             for line in parcel:
-                if (line.get('product')
-                        and line.get('quantity')
-                        and line.get('unit')):
-                    product = id2product[line['product']]
-                    quantity = uom_obj.compute_qty(id2unit[line['unit']],
-                        line['quantity'], product.default_uom, round=False)
-                    if product.weight:
-                        weight += uom_obj.compute_qty(product.weight_uom,
-                            product.weight * quantity, carrier.weight_uom,
-                            round=False)
+                if line.product and line.quantity and line.unit:
+                    quantity = Uom.compute_qty(line.unit, line.quantity,
+                        line.product.default_uom, round=False)
+                    if line.product.weight:
+                        weight += Uom.compute_qty(line.product.weight_uom,
+                            line.product.weight * quantity,
+                            self.carrier.weight_uom, round=False)
             weights.append(weight)
         return context
-
-Sale()
diff -r b7bad438669f trytond/modules/carrier_weight/stock.py
--- a/trytond/modules/carrier_weight/stock.py	Thu Sep 13 12:41:15 2012 +0200
+++ b/trytond/modules/carrier_weight/stock.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,14 +1,16 @@
 #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 izip, groupby
+from itertools import groupby
 from functools import partial
 
-from trytond.model import Model
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['ShipmentIn', 'ShipmentOut']
+__metaclass__ = PoolMeta
 
-class ShipmentIn(Model):
-    _name = 'stock.shipment.in'
+
+class ShipmentIn:
+    __name__ = 'stock.shipment.in'
 
     def _group_parcel_key(self, lines, line):
         """
@@ -16,53 +18,37 @@
         """
         return None
 
-    def _get_carrier_context(self, values):
-        pool = Pool()
-        carrier_obj = pool.get('carrier')
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
+    def _get_carrier_context(self):
+        Uom = Pool().get('product.uom')
 
-        context = super(ShipmentIn, self)._get_carrier_context(values)
-        if not values.get('carrier'):
+        context = super(ShipmentIn, self)._get_carrier_context()
+        if not self.carrier:
             return context
-        carrier = carrier_obj.browse(values['carrier'])
-        if carrier.carrier_cost_method != 'weight':
+        if self.carrier.carrier_cost_method != 'weight':
             return context
         context = context.copy()
         weights = []
         context['weights'] = weights
-        product_ids = [line['product']
-            for line in values.get('incoming_moves') or []
-            if line.get('product')]
-        id2product = dict((p.id, p) for p in product_obj.browse(product_ids))
-        uom_ids = [line['uom'] for line in values.get('incoming_moves') or []
-            if line.get('uom')]
-        id2uom = dict((u.id, u) for u in uom_obj.browse(uom_ids))
 
-        lines = values.get('incoming_moves') or []
+        lines = self.incoming_moves or []
         keyfunc = partial(self._group_parcel_key, lines)
         lines = sorted(lines, key=keyfunc)
 
         for key, parcel in groupby(lines, key=keyfunc):
             weight = 0
             for line in parcel:
-                if (line.get('product')
-                        and line.get('quantity')
-                        and line.get('uom')):
-                    product = id2product[line['product']]
-                    quantity = uom_obj.compute_qty(id2uom[line['uom']],
-                        line['quantity'], product.default_uom, round=False)
-                    weight += uom_obj.compute_qty(product.weight_uom,
-                        product.weight * quantity, carrier.weight_uom,
-                        round=False)
+                if line.product and line.quantity and line.uom:
+                    quantity = Uom.compute_qty(line.uom, line.quantity,
+                        line.product.default_uom, round=False)
+                    weight += Uom.compute_qty(line.product.weight_uom,
+                        line.product.weight * quantity,
+                        self.carrier.weight_uom, round=False)
             weights.append(weight)
         return context
 
-ShipmentIn()
 
-
-class ShipmentOut(Model):
-    _name = 'stock.shipment.out'
+class ShipmentOut:
+    __name__ = 'stock.shipment.out'
 
     def _group_parcel_key(self, lines, line):
         """
@@ -70,61 +56,30 @@
         """
         return None
 
-    def _get_carrier_context(self, values):
-        pool = Pool()
-        carrier_obj = pool.get('carrier')
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
+    def _get_carrier_context(self):
+        Uom = Pool().get('product.uom')
 
-        context = super(ShipmentOut, self)._get_carrier_context(values)
-        if not values.get('carrier'):
+        context = super(ShipmentOut, self)._get_carrier_context()
+        if not self.carrier:
             return context
-        carrier = carrier_obj.browse(values['carrier'])
-        if carrier.carrier_cost_method != 'weight':
+        if self.carrier.carrier_cost_method != 'weight':
             return context
         context = context.copy()
         weights = []
         context['weights'] = weights
-        product_ids = [line['product']
-            for line in values.get('inventory_moves') or []
-            if line.get('product')]
-        id2product = dict((p.id, p) for p in product_obj.browse(product_ids))
-        uom_ids = [line['uom'] for line in values.get('inventory_moves') or []
-            if line.get('uom')]
-        id2uom = dict((u.id, u) for u in uom_obj.browse(uom_ids))
 
-        lines = values.get('inventory_moves') or []
+        lines = self.inventory_moves or []
         keyfunc = partial(self._group_parcel_key, lines)
         lines = sorted(lines, key=keyfunc)
 
         for key, parcel in groupby(lines, key=keyfunc):
             weight = 0
             for line in parcel:
-                if (line.get('product')
-                        and line.get('quantity')
-                        and line.get('uom')):
-                    product = id2product[line['product']]
-                    quantity = uom_obj.compute_qty(id2uom[line['uom']],
-                        line['quantity'], product.default_uom, round=False)
-                    weight += uom_obj.compute_qty(product.weight_uom,
-                        product.weight * quantity, carrier.weight_uom,
-                        round=False)
+                if line.product and line.quantity and line.uom:
+                    quantity = Uom.compute_qty(line.uom, line.quantity,
+                        line.product.default_uom, round=False)
+                    weight += Uom.compute_qty(line.product.weight_uom,
+                        line.product.weight * quantity,
+                        self.carrier.weight_uom, round=False)
             weights.append(weight)
         return context
-
-    def get_carrier_context(self, shipment, values=None):
-        if values is None:
-            values = {}
-        values = values.copy()
-        inventory_moves = [{
-                'product': m.product.id,
-                'quantity': m.quantity,
-                'uom': m.uom.id,
-                } for m in shipment.inventory_moves]
-        values.setdefault('inventory_moves', inventory_moves)
-        for new, old in izip(inventory_moves, values['inventory_moves']):
-            old.update(new)
-        return super(ShipmentOut, self).get_carrier_context(shipment,
-            values=values)
-
-ShipmentOut()
diff -r b7bad438669f trytond/modules/carrier_weight/tests/test_carrier_weight.py
--- a/trytond/modules/carrier_weight/tests/test_carrier_weight.py	Thu Sep 13 12:41:15 2012 +0200
+++ b/trytond/modules/carrier_weight/tests/test_carrier_weight.py	Mon Sep 24 20:36:40 2012 +0200
@@ -50,37 +50,36 @@
         Test compute_weight_price.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            party_id = self.party.create({
+            party = self.party.create({
                     'name': 'Carrier',
                     })
-            uom_id, = self.uom.search([
+            uom, = self.uom.search([
                     ('name', '=', 'Unit'),
                     ])
-            category_id = self.category.create({
+            category = self.category.create({
                     'name': 'Category',
                     })
-            product_id = self.product.create({
+            product = self.product.create({
                     'name': 'Carrier',
-                    'default_uom': uom_id,
-                    'category': category_id,
+                    'default_uom': uom.id,
+                    'category': category.id,
                     'type': 'service',
                     'list_price': Decimal(0),
                     'cost_price': Decimal(0),
                     })
-            weight_uom_id, = self.uom.search([
+            weight_uom, = self.uom.search([
                     ('name', '=', 'Kilogram'),
                     ])
-            currency_id, = self.currency.search([
+            currency, = self.currency.search([
                     ('code', '=', 'cu1'),
                     ])
-            carrier_id = self.carrier.create({
-                    'party': party_id,
-                    'carrier_product': product_id,
+            carrier = self.carrier.create({
+                    'party': party.id,
+                    'carrier_product': product.id,
                     'carrier_cost_method': 'weight',
-                    'weight_uom': weight_uom_id,
-                    'weight_currency': currency_id,
+                    'weight_uom': weight_uom.id,
+                    'weight_currency': currency.id,
                     })
-            carrier = self.carrier.browse(carrier_id)
             for i, weight in enumerate(xrange(0, 100, 20)):
                 self.weight_price_list.create({
                         'carrier': carrier.id,
@@ -88,7 +87,7 @@
                         'price': Decimal(i),
                         })
             self.assertEqual(
-                self.carrier.compute_weight_price(carrier, 0), Decimal(0))
+                carrier.compute_weight_price(0), Decimal(0))
             for weight, price in [
                     (1, Decimal(0)),
                     (10, Decimal(0)),
@@ -98,8 +97,7 @@
                     (81, Decimal(4)),
                     (100, Decimal(4)),
                     ]:
-                self.assertEqual(
-                    self.carrier.compute_weight_price(carrier, weight), price)
+                self.assertEqual(carrier.compute_weight_price(weight), price)
 
 
 def doctest_dropdb(test):
diff -r bfedb8605d86 trytond/modules/company/__init__.py
--- a/trytond/modules/company/__init__.py	Fri Sep 21 15:20:25 2012 +0200
+++ b/trytond/modules/company/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,6 +1,29 @@
 #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.pool import Pool
 from .company import *
 from .cron import *
-from . import party
+from .party import *
+
+
+def register():
+    Pool.register(
+        Company,
+        Employee,
+        UserEmployee,
+        User,
+        Property,
+        Sequence,
+        SequenceStrict,
+        CompanyConfigStart,
+        Cron,
+        CronCompany,
+        PartyConfiguration,
+        module='company', type_='model')
+    Pool.register(
+        CompanyConfig,
+        module='company', type_='wizard')
+    Pool.register(
+        LetterReport,
+        module='company', type_='report')
diff -r bfedb8605d86 trytond/modules/company/company.py
--- a/trytond/modules/company/company.py	Fri Sep 21 15:20:25 2012 +0200
+++ b/trytond/modules/company/company.py	Mon Sep 24 20:36:40 2012 +0200
@@ -6,15 +6,18 @@
 from trytond.report import Report
 from trytond.pyson import Eval, If
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
+
+__all__ = ['Company', 'Employee', 'UserEmployee', 'User', 'Property',
+    'Sequence', 'SequenceStrict', 'CompanyConfigStart', 'CompanyConfig',
+    'CompanyReport', 'LetterReport']
+__metaclass__ = PoolMeta
 
 
 class Company(ModelSQL, ModelView):
     'Company'
-    _name = 'company.company'
-    _description = __doc__
+    __name__ = 'company.company'
     _inherits = {'party.party': 'party'}
-
     party = fields.Many2One('party.party', 'Party', required=True,
             ondelete='CASCADE')
     parent = fields.Many2One('company.company', 'Parent')
@@ -24,70 +27,57 @@
     currency = fields.Many2One('currency.currency', 'Currency', required=True)
     employees = fields.One2Many('company.employee', 'company', 'Employees')
 
-    def __init__(self):
-        super(Company, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Company, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_companies'),
-        ]
-        self._error_messages.update({
-            'recursive_companies': 'You can not create recursive companies!',
-        })
+            ]
+        cls._error_messages.update({
+                'recursive_companies': \
+                    'You can not create recursive companies!',
+                })
 
-    def copy(self, ids, default=None):
-        party_obj = Pool().get('party.party')
+    @classmethod
+    def copy(cls, companies, default=None):
+        Party = Pool().get('party.party')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for company in self.browse(ids):
-            default['party'] = party_obj.copy(company.party.id)
-            new_id = super(Company, self).copy(company.id, default=default)
-            new_ids.append(new_id)
+        new_companies = []
+        for company in companies:
+            default['party'], = Party.copy([company.party])
+            new_company, = super(Company, cls).copy([company], default=default)
+            new_companies.append(new_company)
+        return new_companies
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-    def write(self, ids, vals):
-        res = super(Company, self).write(ids, vals)
+    @classmethod
+    def write(cls, companies, vals):
+        super(Company, cls).write(companies, vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-Company()
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
 
 class Employee(ModelSQL, ModelView):
     'Employee'
-    _name = 'company.employee'
-    _description = __doc__
+    __name__ = 'company.employee'
     _inherits = {'party.party': 'party'}
-
     party = fields.Many2One('party.party', 'Party', required=True)
     company = fields.Many2One('company.company', 'Company', required=True)
 
-Employee()
-
 
 class UserEmployee(ModelSQL):
     'User - Employee'
-    _name = 'res.user-company.employee'
-    _description = __doc__
+    __name__ = 'res.user-company.employee'
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE', select=True,
         required=True)
     employee = fields.Many2One('company.employee', 'Employee',
         ondelete='CASCADE', select=True, required=True)
 
-UserEmployee()
 
-
-class User(ModelSQL, ModelView):
-    _name = 'res.user'
+class User:
+    __name__ = 'res.user'
     main_company = fields.Many2One('company.company', 'Main Company',
         on_change=['main_company'])
     company = fields.Many2One('company.company', 'Current Company',
@@ -104,87 +94,94 @@
             ],
         depends=['company', 'employees'])
 
-    def __init__(self):
-        super(User, self).__init__()
-        self._context_fields.insert(0, 'company')
-        self._context_fields.insert(0, 'employee')
+    @classmethod
+    def __setup__(cls):
+        super(User, cls).__setup__()
+        cls._context_fields.insert(0, 'company')
+        cls._context_fields.insert(0, 'employee')
 
-    def default_main_company(self):
+    @staticmethod
+    def default_main_company():
         return Transaction().context.get('company')
 
-    def default_company(self):
-        return self.default_main_company()
+    @classmethod
+    def default_company(cls):
+        return cls.default_main_company()
 
-    def get_companies(self, ids, name):
-        company_obj = Pool().get('company.company')
-        res = {}
+    @classmethod
+    def get_companies(cls, users, name):
+        Company = Pool().get('company.company')
+        companies = {}
         company_childs = {}
-        for user in self.browse(ids):
-            res[user.id] = []
-            company_id = None
+        for user in users:
+            companies[user.id] = []
+            company = None
             if user.company:
-                company_id = user.company.id
+                company = user.company
             elif user.main_company:
-                company_id = user.main_company.id
-            if company_id:
-                if company_id in company_childs:
-                    company_ids = company_childs[company_id]
+                company = user.main_company
+            if company:
+                if company in company_childs:
+                    company_ids = company_childs[company]
                 else:
-                    company_ids = company_obj.search([
-                        ('parent', 'child_of', [company_id]),
-                        ])
-                    company_childs[company_id] = company_ids
+                    company_ids = map(int, Company.search([
+                                ('parent', 'child_of', [company.id]),
+                                ]))
+                    company_childs[company] = company_ids
                 if company_ids:
-                    res[user.id].extend(company_ids)
-        return res
+                    companies[user.id].extend(company_ids)
+        return companies
 
-    def get_status_bar(self, ids, name):
-        res = super(User, self).get_status_bar(ids, name)
-        for user in self.browse(ids):
-            if user.company:
-                res[user.id] += ' - %s [%s]' % (user.company.name,
-                    user.company.currency.name)
-        return res
+    def get_status_bar(self, name):
+        status = super(User, self).get_status_bar(name)
+        if self.company:
+            status += ' - %s [%s]' % (self.company.name,
+                self.company.currency.name)
+        return status
 
-    def on_change_main_company(self, vals):
+    def on_change_main_company(self):
         return {
-            'company': vals.get('main_company'),
+            'company': self.main_company.id if self.main_company else None,
             'employee': None,
             }
 
-    def on_change_company(self, values):
-        employee_obj = Pool().get('company.employee')
+    def on_change_company(self):
+        Employee = Pool().get('company.employee')
         result = {
             'employee': None,
             }
-        if values.get('company') and values.get('employees'):
-            employee_ids = employee_obj.search([
-                    ('id', 'in', [e['id'] for e in values['employees']]),
-                    ('company', '=', values['company']),
+        if self.company and self.employees:
+            employees = Employee.search([
+                    ('id', 'in', [e.id for e in self.employees]),
+                    ('company', '=', self.company.id),
                     ])
-            if employee_ids:
-                result['employee'] = employee_ids[0]
+            if employees:
+                result['employee'] = employees[0].id
         return result
 
-    def _get_preferences(self, user, context_only=False):
-        res = super(User, self)._get_preferences(user,
-                context_only=context_only)
+    @classmethod
+    def _get_preferences(cls, user, context_only=False):
+        res = super(User, cls)._get_preferences(user,
+            context_only=context_only)
         if not context_only:
-            res['main_company'] = user.main_company.id
-            if user.main_company.id:
+            res['main_company'] = None
+            if user.main_company:
+                res['main_company'] = user.main_company.id
                 res['main_company.rec_name'] = user.main_company.rec_name
             res['employees'] = [e.id for e in user.employees]
         if user.employee:
-            res['employee'] = user.employee.id
-            if user.employee.id:
+            res['employee'] = None
+            if user.employee:
+                res['employee'] = user.employee.id
                 res['employee.rec_name'] = user.employee.rec_name
         return res
 
-    def get_preferences_fields_view(self):
+    @classmethod
+    def get_preferences_fields_view(cls):
         pool = Pool()
-        company_obj = pool.get('company.company')
+        Company = pool.get('company.company')
 
-        res = super(User, self).get_preferences_fields_view()
+        res = super(User, cls).get_preferences_fields_view()
         res = copy.deepcopy(res)
 
         def convert2selection(definition, name):
@@ -196,114 +193,105 @@
 
         if 'company' in res['fields']:
             selection = convert2selection(res['fields'], 'company')
-            user = self.browse(Transaction().user)
-            company_ids = company_obj.search([
-                    ('parent', 'child_of', [user.main_company.id], 'parent'),
-                    ])
-            for company in company_obj.browse(company_ids):
-                selection.append((company.id, company.rec_name))
+            user = cls(Transaction().user)
+            if user.main_company:
+                companies = Company.search([
+                        ('parent', 'child_of', [user.main_company.id],
+                            'parent'),
+                        ])
+                for company in companies:
+                    selection.append((company.id, company.rec_name))
         return res
 
-    def read(self, ids, fields_names=None):
-        company_obj = Pool().get('company.company')
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        Company = Pool().get('company.company')
         user_id = Transaction().user
         if user_id == 0 and 'user' in Transaction().context:
             user_id = Transaction().context['user']
-        result = super(User, self).read(ids, fields_names=fields_names)
+        result = super(User, cls).read(ids, fields_names=fields_names)
         if (fields_names
                 and 'company' in fields_names
                 and 'company' in Transaction().context):
             values = None
-            if isinstance(ids, (int, long)):
-                if int(user_id) == ids:
-                    values = result
-            else:
-                if int(user_id) in ids:
-                    for vals in result:
-                        if vals['id'] == int(user_id):
-                            values = vals
-                            break
+            if int(user_id) in ids:
+                for vals in result:
+                    if vals['id'] == int(user_id):
+                        values = vals
+                        break
             if values:
                 main_company_id = values.get('main_company')
                 if not main_company_id:
-                    main_company_id = self.read(user_id,
-                        ['main_company'])['main_company']
-                companies = company_obj.search([
-                    ('parent', 'child_of', [main_company_id]),
-                ])
+                    main_company_id = cls.read([user_id],
+                        ['main_company'])[0]['main_company']
+                companies = Company.search([
+                        ('parent', 'child_of', [main_company_id]),
+                        ])
                 company_id = Transaction().context['company']
-                if ((company_id and company_id in companies)
+                if ((company_id and company_id in map(int, companies))
                         or not company_id):
                     values['company'] = company_id
         return result
 
-User()
 
-
-class Property(ModelSQL, ModelView):
-    _name = 'ir.property'
+class Property:
+    __name__ = 'ir.property'
     company = fields.Many2One('company.company', 'Company',
         domain=[
             ('id', If(Eval('context', {}).contains('company'), '=', '!='),
                 Eval('context', {}).get('company', 0)),
             ])
 
-    def _set_values(self, model, res_id, val, field_id):
-        user_obj = Pool().get('res.user')
+    @classmethod
+    def _set_values(cls, model, res_id, val, field_id):
+        User = Pool().get('res.user')
         user_id = Transaction().user
         if user_id == 0:
             user_id = Transaction().context.get('user', user_id)
-        user = user_obj.browse(user_id)
-        res = super(Property, self)._set_values(model, res_id, val, field_id)
-        if user:
+        user = User(user_id)
+        res = super(Property, cls)._set_values(model, res_id, val, field_id)
+        if user and user.company:
             res['company'] = user.company.id
         return res
 
-    def search(self, domain, offset=0, limit=None, order=None, count=False):
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False):
         if Transaction().user == 0 and not 'user' in Transaction().context:
             domain = ['AND', domain[:], ('company', '=', None)]
-        return super(Property, self).search(domain, offset=offset,
+        return super(Property, cls).search(domain, offset=offset,
                 limit=limit, order=order, count=count)
 
-Property()
 
-
-class Sequence(ModelSQL, ModelView):
-    _name = 'ir.sequence'
+class Sequence:
+    __name__ = 'ir.sequence'
     company = fields.Many2One('company.company', 'Company',
         domain=[
             ('id', If(Eval('context', {}).contains('company'), '=', '!='),
                 Eval('context', {}).get('company', 0)),
             ])
 
-    def __init__(self):
-        super(Sequence, self).__init__()
-        self._order.insert(0, ('company', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(Sequence, cls).__setup__()
+        cls._order.insert(0, ('company', 'ASC'))
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-Sequence()
-
 
 class SequenceStrict(Sequence):
-    _name = 'ir.sequence.strict'
-
-SequenceStrict()
+    __name__ = 'ir.sequence.strict'
 
 
 class CompanyConfigStart(ModelView):
     'Company Config'
-    _name = 'company.company.config.start'
-    _description = __doc__
-
-CompanyConfigStart()
+    __name__ = 'company.company.config.start'
 
 
 class CompanyConfig(Wizard):
     'Configure Company'
-    _name = 'company.company.config'
-
+    __name__ = 'company.company.config'
     start = StateView('company.company.config.start',
         'company.company_config_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -316,48 +304,29 @@
             ])
     add = StateTransition()
 
-    def transition_add(self, session):
-        company_obj = Pool().get('company.company')
-        user_obj = Pool().get('res.user')
+    def transition_add(self):
+        User = Pool().get('res.user')
 
-        values = session.data['company'].copy()
-        for fname in values.keys():
-            if fname in ('id', 'party'):
-                del values[fname]
-                continue
-            if fname in company_obj._columns:
-                field = company_obj._columns[fname]
-            else:
-                field = company_obj._inherit_fields[fname][2]
-            if field._type == 'one2many':
-                values[fname] = [('create', v) for v in values[fname]]
-            elif field._type == 'many2many':
-                values[fname] = [('set', [v['id'] for v in values[fname]])]
-        company_id = company_obj.create(values)
-        user_ids = user_obj.search([
-            ('main_company', '=', None),
-            ])
-        user_obj.write(user_ids, {
-            'main_company': company_id,
-            'company': company_id,
-            })
+        self.company.save()
+        users = User.search([
+                ('main_company', '=', None),
+                ])
+        User.write(users, {
+                'main_company': self.company.id,
+                'company': self.company.id,
+                })
         return 'end'
 
-CompanyConfig()
-
 
 class CompanyReport(Report):
 
-    def parse(self, report, objects, datas, localcontext=None):
-        user = Pool().get('res.user').browse(Transaction().user)
-        if localcontext is None:
-            localcontext = {}
+    @classmethod
+    def parse(cls, report, records, data, localcontext):
+        user = Pool().get('res.user')(Transaction().user)
         localcontext['company'] = user.company
-        return super(CompanyReport, self).parse(report, objects, datas,
-                localcontext=localcontext)
+        return super(CompanyReport, cls).parse(report, records, data,
+            localcontext)
 
 
 class LetterReport(CompanyReport):
-    _name = 'party.letter'
-
-LetterReport()
+    __name__ = 'party.letter'
diff -r bfedb8605d86 trytond/modules/company/cron.py
--- a/trytond/modules/company/cron.py	Fri Sep 21 15:20:25 2012 +0200
+++ b/trytond/modules/company/cron.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,47 +1,48 @@
 #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 ModelView, ModelSQL, fields
-from trytond.pool import Pool
+from trytond.model import ModelSQL, fields
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 
 
-class Cron(ModelSQL, ModelView):
-    "Cron"
-    _name = "ir.cron"
+__all__ = ['Cron', 'CronCompany']
+__metaclass__ = PoolMeta
+
+
+class Cron:
+    __name__ = "ir.cron"
     companies = fields.Many2Many('ir.cron-company.company', 'cron', 'company',
             'Companies', help='Companies registered for this cron')
 
-    def _callback(self, cron):
-        user_obj = Pool().get('res.user')
+    @classmethod
+    def _callback(cls, cron):
+        User = Pool().get('res.user')
         if not cron.companies:
-            return super(Cron, self)._callback(cron)
+            return super(Cron, cls)._callback(cron)
         # TODO replace with context
         for company in cron.companies:
-            user_obj.write(cron.user.id, {
-                'company': company.id,
-                'main_company': company.id,
-            })
+            User.write([cron.user], {
+                    'company': company.id,
+                    'main_company': company.id,
+                    })
             with Transaction().set_context(company=company.id):
-                super(Cron, self)._callback(cron)
-        user_obj.write(cron.user.id, {
-            'company': None,
-            'main_company': None,
-        })
+                super(Cron, cls)._callback(cron)
+        User.write([cron.user], {
+                'company': None,
+                'main_company': None,
+                })
 
-    def default_companies(self):
-        company_obj = Pool().get('company.company')
-        return company_obj.search([])
-
-Cron()
+    @staticmethod
+    def default_companies():
+        Company = Pool().get('company.company')
+        return map(int, Company.search([]))
 
 
 class CronCompany(ModelSQL):
     'Cron - Company'
-    _name = 'ir.cron-company.company'
+    __name__ = 'ir.cron-company.company'
     _table = 'cron_company_rel'
     cron = fields.Many2One('ir.cron', 'Cron', ondelete='CASCADE',
             required=True, select=True)
     company = fields.Many2One('company.company', 'Company', ondelete='CASCADE',
             required=True, select=True)
-
-CronCompany()
diff -r bfedb8605d86 trytond/modules/company/party.py
--- a/trytond/modules/company/party.py	Fri Sep 21 15:20:25 2012 +0200
+++ b/trytond/modules/company/party.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,21 +1,20 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-import copy
+from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 
-from trytond.model import Model
-from trytond.pyson import Eval
+__all__ = ['PartyConfiguration']
+__metaclass__ = PoolMeta
 
 
-class Configuration(Model):
-    _name = 'party.configuration'
+class PartyConfiguration:
+    __name__ = 'party.configuration'
 
-    def __init__(self):
-        super(Configuration, self).__init__()
+    @classmethod
+    def __setup__(cls):
+        super(PartyConfiguration, cls).__setup__()
 
-        self.party_sequence = copy.copy(self.party_sequence)
-        self.party_sequence.domain = copy.copy(self.party_sequence.domain)
-        self.party_sequence.domain = [
-            self.party_sequence.domain,
-            ('company', 'in', [Eval('context', {}).get('company'), None])]
-
-Configuration()
+        cls.party_sequence.domain = [
+            cls.party_sequence.domain,
+            ('company', 'in', [Eval('context', {}).get('company'), None]),
+            ]
diff -r bfedb8605d86 trytond/modules/company/tests/test_company.py
--- a/trytond/modules/company/tests/test_company.py	Fri Sep 21 15:20:25 2012 +0200
+++ b/trytond/modules/company/tests/test_company.py	Mon Sep 24 20:36:40 2012 +0200
@@ -45,15 +45,15 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            currency1_id = self.currency.search([
-                ('code', '=', 'cu1'),
-                ], 0, 1, None)[0]
+            currency1, = self.currency.search([
+                    ('code', '=', 'cu1'),
+                    ], 0, 1, None)
 
-            company1_id = self.company.create({
-                'name': 'B2CK',
-                'currency': currency1_id,
-                })
-            self.assert_(company1_id)
+            company1 = self.company.create({
+                    'name': 'B2CK',
+                    'currency': currency1.id,
+                    })
+            self.assert_(company1)
             transaction.cursor.commit()
 
     def test0020company_recursion(self):
@@ -61,24 +61,24 @@
         Test company recursion.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            currency1_id = self.currency.search([
+            currency1, = self.currency.search([
                 ('code', '=', 'cu1'),
-                ], 0, 1, None)[0]
+                ], 0, 1, None)
 
-            company1_id = self.company.search([
-                ('name', '=', 'B2CK'),
-                ], 0, 1, None)[0]
+            company1, = self.company.search([
+                    ('name', '=', 'B2CK'),
+                    ], 0, 1, None)
 
-            company2_id = self.company.create({
-                'name': 'B2CK Branch',
-                'parent': company1_id,
-                'currency': currency1_id,
-                })
-            self.assert_(company2_id)
+            company2 = self.company.create({
+                    'name': 'B2CK Branch',
+                    'parent': company1.id,
+                    'currency': currency1.id,
+                    })
+            self.assert_(company2)
 
-            self.failUnlessRaises(Exception, self.company.write,
-                    company1_id, {
-                        'parent': company2_id,
+            self.assertRaises(Exception, self.company.write,
+                [company1], {
+                    'parent': company2.id,
                     })
 
     def test0030employe(self):
@@ -87,14 +87,14 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            company1_id = self.company.search([
-                ('name', '=', 'B2CK'),
-                ], 0, 1, None)[0]
+            company1, = self.company.search([
+                    ('name', '=', 'B2CK'),
+                    ], 0, 1, None)
 
             self.employee.create({
-                'name': 'Employee1',
-                'company': company1_id,
-                })
+                    'name': 'Employee1',
+                    'company': company1.id,
+                    })
             transaction.cursor.commit()
 
     def test0040user(self):
@@ -103,58 +103,47 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            currency1_id = self.currency.search([
-                ('code', '=', 'cu1'),
-                ], 0, 1, None)[0]
+            currency1, = self.currency.search([
+                    ('code', '=', 'cu1'),
+                    ], 0, 1, None)
 
-            company1_id = self.company.search([
-                ('name', '=', 'B2CK'),
-                ], 0, 1, None)[0]
+            company1, = self.company.search([
+                    ('name', '=', 'B2CK'),
+                    ], 0, 1, None)
 
-            company2_id = self.company.create({
-                'name': 'B2CK Branch',
-                'parent': company1_id,
-                'currency': currency1_id,
-                })
-            user1_id = self.user.create({
-                'name': 'Test 1',
-                'login': 'test1',
-                'main_company': company1_id,
-                'company': company1_id,
-            })
-            user2_id = self.user.create({
-                'name': 'Test 2',
-                'login': 'test2',
-                'main_company': company2_id,
-                'company': company2_id,
-            })
-            self.assert_(user1_id)
+            company2 = self.company.create({
+                    'name': 'B2CK Branch',
+                    'parent': company1.id,
+                    'currency': currency1.id,
+                    })
+            user1 = self.user.create({
+                    'name': 'Test 1',
+                    'login': 'test1',
+                    'main_company': company1.id,
+                    'company': company1.id,
+                    })
+            user2 = self.user.create({
+                    'name': 'Test 2',
+                    'login': 'test2',
+                    'main_company': company2.id,
+                    'company': company2.id,
+                    })
+            self.assert_(user1)
 
-            with transaction.set_user(user1_id):
-                company_id = self.user.read(user1_id, ['company'])['company']
-                self.assertEqual(company_id, company1_id)
+            with transaction.set_user(user1.id):
+                user1, user2 = self.user.browse([user1.id, user2.id])
+                self.assertEqual(user1.company, company1)
+                self.assertEqual(user2.company, company2)
 
-                user1, user2 = self.user.browse([user1_id, user2_id])
-                self.assertEqual(user1.company.id, company1_id)
-                self.assertEqual(user2.company.id, company2_id)
+                with transaction.set_context({'company': company2.id}):
+                    user1, user2 = self.user.browse([user1.id, user2.id])
+                    self.assertEqual(user1.company, company2)
+                    self.assertEqual(user2.company, company2)
 
-                with transaction.set_context({'company': company2_id}):
-                    company_id = self.user.read(user1_id,
-                        ['company'])['company']
-                    self.assertEqual(company_id, company2_id)
-
-                    user1, user2 = self.user.browse([user1_id, user2_id])
-                    self.assertEqual(user1.company.id, company2_id)
-                    self.assertEqual(user2.company.id, company2_id)
-
-                with transaction.set_context({'company': False}):
-                    company_id = self.user.read(user1_id,
-                        ['company'])['company']
-                    self.assertEqual(company_id, False)
-
-                    user1, user2 = self.user.browse([user1_id, user2_id])
-                    self.assertEqual(user1.company.id, None)
-                    self.assertEqual(user2.company.id, company2_id)
+                with transaction.set_context({'company': None}):
+                    user1, user2 = self.user.browse([user1.id, user2.id])
+                    self.assertEqual(user1.company, None)
+                    self.assertEqual(user2.company, company2)
 
 
 def suite():
diff -r f8a1da795c36 trytond/modules/company_work_time/__init__.py
--- a/trytond/modules/company_work_time/__init__.py	Fri Jun 08 19:33:56 2012 +0200
+++ b/trytond/modules/company_work_time/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,5 +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.
 
+from trytond.pool import Pool
 from .company import *
 from .res import *
+
+
+def register():
+    Pool.register(
+        Company,
+        User,
+        module='company_work_time', type_='model')
diff -r f8a1da795c36 trytond/modules/company_work_time/company.py
--- a/trytond/modules/company_work_time/company.py	Fri Jun 08 19:33:56 2012 +0200
+++ b/trytond/modules/company_work_time/company.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,27 +1,33 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-"Company"
-from trytond.model import ModelView, ModelSQL, fields
+from trytond.model import fields
+from trytond.pool import PoolMeta
 
 
-class Company(ModelSQL, ModelView):
-    _name = 'company.company'
+__all__ = ['Company']
+__metaclass__ = PoolMeta
+
+
+class Company:
+    __name__ = 'company.company'
 
     hours_per_work_day = fields.Float("Hours per Work Day", required=True)
     hours_per_work_week = fields.Float("Hours per Work Week", required=True)
     hours_per_work_month = fields.Float("Hours per Work Month", required=True)
     hours_per_work_year = fields.Float("Hours per Work Year", required=True)
 
-    def default_hours_per_work_day(self):
+    @staticmethod
+    def default_hours_per_work_day():
         return 8
 
-    def default_hours_per_work_week(self):
+    @staticmethod
+    def default_hours_per_work_week():
         return 40
 
-    def default_hours_per_work_month(self):
+    @staticmethod
+    def default_hours_per_work_month():
         return 160
 
-    def default_hours_per_work_year(self):
+    @staticmethod
+    def default_hours_per_work_year():
         return 1920
-
-Company()
diff -r f8a1da795c36 trytond/modules/company_work_time/res.py
--- a/trytond/modules/company_work_time/res.py	Fri Jun 08 19:33:56 2012 +0200
+++ b/trytond/modules/company_work_time/res.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,13 +1,18 @@
 #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 ModelView, ModelSQL
+from trytond.pool import PoolMeta
 
 
-class User(ModelSQL, ModelView):
-    _name = 'res.user'
+__all__ = ['User']
+__metaclass__ = PoolMeta
 
-    def _get_preferences(self, user, context_only=False):
-        res = super(User, self)._get_preferences(user,
+
+class User:
+    __name__ = 'res.user'
+
+    @classmethod
+    def _get_preferences(cls, user, context_only=False):
+        res = super(User, cls)._get_preferences(user,
             context_only=context_only)
         if user.company:
             res['company_work_time'] = {
@@ -17,5 +22,3 @@
                 'd': user.company.hours_per_work_day,
             }
         return res
-
-User()
diff -r 1632e38eb821 trytond/modules/country/__init__.py
--- a/trytond/modules/country/__init__.py	Tue Aug 21 11:05:37 2012 +0200
+++ b/trytond/modules/country/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,4 +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.pool import Pool
 from .country import *
+
+
+def register():
+    Pool.register(
+        Country,
+        Subdivision,
+        module='country', type_='model')
diff -r 1632e38eb821 trytond/modules/country/country.py
--- a/trytond/modules/country/country.py	Tue Aug 21 11:05:37 2012 +0200
+++ b/trytond/modules/country/country.py	Mon Sep 24 20:36:40 2012 +0200
@@ -2,11 +2,12 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelView, ModelSQL, fields
 
+__all__ = ['Country', 'Subdivision']
+
 
 class Country(ModelSQL, ModelView):
     'Country'
-    _name = 'country.country'
-    _description = __doc__
+    __name__ = 'country.country'
     name = fields.Char('Name', required=True, translate=True,
            help='The full name of the country.', select=True)
     code = fields.Char('Code', size=2, select=True,
@@ -15,41 +16,41 @@
     subdivisions = fields.One2Many('country.subdivision',
             'country', 'Subdivisions')
 
-    def __init__(self):
-        super(Country, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Country, cls).__setup__()
+        cls._sql_constraints += [
             ('name_uniq', 'UNIQUE(name)',
-             'The name of the country must be unique!'),
+                'The name of the country must be unique!'),
             ('code_uniq', 'UNIQUE(code)',
-             'The code of the country must be unique!'),
-        ]
-        self._order.insert(0, ('name', 'ASC'))
+                'The code of the country must be unique!'),
+            ]
+        cls._order.insert(0, ('name', 'ASC'))
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([('code',) + clause[1:]], limit=1)
-        if ids:
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        if cls.search([('code',) + clause[1:]], limit=1):
             return [('code',) + clause[1:]]
-        return [(self._rec_name,) + clause[1:]]
+        return [(cls._rec_name,) + clause[1:]]
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         if 'code' in vals and vals['code']:
             vals = vals.copy()
             vals['code'] = vals['code'].upper()
-        return super(Country, self).create(vals)
+        return super(Country, cls).create(vals)
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, countries, vals):
         if 'code' in vals and vals['code']:
             vals = vals.copy()
             vals['code'] = vals['code'].upper()
-        return super(Country, self).write(ids, vals)
-
-Country()
+        super(Country, cls).write(countries, vals)
 
 
 class Subdivision(ModelSQL, ModelView):
     "Subdivision"
-    _name = 'country.subdivision'
-    _description = __doc__
+    __name__ = 'country.subdivision'
     country = fields.Many2One('country.country', 'Country',
             required=True, select=True)
     name = fields.Char('Name', required=True, select=True, translate=True)
@@ -139,24 +140,25 @@
         ], 'Type', required=True)
     parent = fields.Many2One('country.subdivision', 'Parent')
 
-    def __init__(self):
-        super(Subdivision, self).__init__()
-        self._order.insert(0, ('code', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(Subdivision, cls).__setup__()
+        cls._order.insert(0, ('code', 'ASC'))
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([('code',) + clause[1:]], limit=1)
-        if ids:
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        if cls.search([('code',) + clause[1:]], limit=1):
             return [('code',) + clause[1:]]
-        return [(self._rec_name,) + clause[1:]]
+        return [(cls._rec_name,) + clause[1:]]
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         if 'code' in vals and vals['code']:
             vals['code'] = vals['code'].upper()
-        return super(Subdivision, self).create(vals)
+        return super(Subdivision, cls).create(vals)
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, subdivisions, vals):
         if 'code' in vals and vals['code']:
             vals['code'] = vals['code'].upper()
-        return super(Subdivision, self).write(ids, vals)
-
-Subdivision()
+        super(Subdivision, cls).write(subdivisions, vals)
diff -r c9e235179e4d trytond/modules/currency/__init__.py
--- a/trytond/modules/currency/__init__.py	Fri Jun 08 19:33:58 2012 +0200
+++ b/trytond/modules/currency/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,4 +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.pool import Pool
 from .currency import *
+
+
+def register():
+    Pool.register(
+        Currency,
+        Rate,
+        module='currency', type_='model')
diff -r c9e235179e4d trytond/modules/currency/currency.py
--- a/trytond/modules/currency/currency.py	Fri Jun 08 19:33:58 2012 +0200
+++ b/trytond/modules/currency/currency.py	Mon Sep 24 20:36:40 2012 +0200
@@ -6,12 +6,14 @@
 from trytond.tools import safe_eval, datetime_strftime
 from trytond.transaction import Transaction
 from trytond.pool import Pool
+from trytond.rpc import RPC
+
+__all__ = ['Currency', 'Rate']
 
 
 class Currency(ModelSQL, ModelView):
     'Currency'
-    _name = 'currency.currency'
-    _description = __doc__
+    __name__ = 'currency.currency'
     name = fields.Char('Name', required=True, translate=True)
     symbol = fields.Char('Symbol', size=10, required=True)
     code = fields.Char('Code', size=3, required=True)
@@ -36,95 +38,112 @@
     p_sep_by_space = fields.Boolean('Positive Separate by Space')
     n_sep_by_space = fields.Boolean('Negative Separate by Space')
 
-    def __init__(self):
-        super(Currency, self).__init__()
-        self._order.insert(0, ('code', 'ASC'))
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Currency, cls).__setup__()
+        cls._order.insert(0, ('code', 'ASC'))
+        cls._constraints += [
             ('check_mon_grouping', 'invalid_mon_grouping'),
-        ]
-        self._error_messages.update({
-            'no_rate': 'No rate found for the currency: %s at the date: %s',
-            'invalid_grouping': 'Invalid Grouping!',
-            })
-        self._rpc.update({
-            'compute': False,
-            })
+            ]
+        cls._error_messages.update({
+                'no_rate': 'No rate found for the currency: ' \
+                    '%s at the date: %s',
+                'invalid_grouping': 'Invalid Grouping!',
+                })
+        cls.__rpc__.update({
+                'compute': RPC(),
+                })
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_rounding(self):
+    @staticmethod
+    def default_rounding():
         return Decimal('0.01')
 
-    def default_digits(self):
+    @staticmethod
+    def default_digits():
         return 2
 
-    def default_mon_grouping(self):
+    @staticmethod
+    def default_mon_grouping():
         return '[]'
 
-    def default_mon_thousands_sep(self):
+    @staticmethod
+    def default_mon_thousands_sep():
         return ','
 
-    def default_mon_decimal_point(self):
+    @staticmethod
+    def default_mon_decimal_point():
         return '.'
 
-    def default_p_sign_posn(self):
+    @staticmethod
+    def default_p_sign_posn():
         return 1
 
-    def default_n_sign_posn(self):
+    @staticmethod
+    def default_n_sign_posn():
         return 1
 
-    def default_negative_sign(self):
+    @staticmethod
+    def default_negative_sign():
         return '-'
 
-    def default_positive_sign(self):
+    @staticmethod
+    def default_positive_sign():
         return ''
 
-    def default_p_cs_precedes(self):
+    @staticmethod
+    def default_p_cs_precedes():
         return True
 
-    def default_n_cs_precedes(self):
+    @staticmethod
+    def default_n_cs_precedes():
         return True
 
-    def default_p_sep_by_space(self):
+    @staticmethod
+    def default_p_sep_by_space():
         return False
 
-    def default_n_sep_by_space(self):
+    @staticmethod
+    def default_n_sep_by_space():
         return False
 
-    def check_mon_grouping(self, ids):
+    def check_mon_grouping(self):
         '''
         Check if mon_grouping is list of numbers
         '''
-        for currency in self.browse(ids):
-            try:
-                grouping = safe_eval(currency.mon_grouping)
-                for i in grouping:
-                    if not isinstance(i, int):
-                        return False
-            except Exception:
-                return False
+        try:
+            grouping = safe_eval(self.mon_grouping)
+            for i in grouping:
+                if not isinstance(i, int):
+                    return False
+        except Exception:
+            return False
         return True
 
-    def check_xml_record(self, ids, values):
+    @classmethod
+    def check_xml_record(cls, records, values):
         return True
 
-    def search_rec_name(self, name, clause):
-        ids = []
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        currencies = None
         field = None
         for field in ('code', 'numeric_code'):
-            ids = self.search([(field,) + clause[1:]], limit=1)
-            if ids:
+            currencies = cls.search([(field,) + clause[1:]], limit=1)
+            if currencies:
                 break
-        if ids:
+        if currencies:
             return [(field,) + clause[1:]]
-        return [(self._rec_name,) + clause[1:]]
+        return [(cls._rec_name,) + clause[1:]]
 
-    def on_change_with_rate(self, vals):
+    def on_change_with_rate(self):
         now = datetime.date.today()
         closer = datetime.date.min
         res = Decimal('0.0')
-        for rate in vals.get('rates', []):
+        for rate in self.rates or []:
             if 'date' not in rate or 'rate' not in rate:
                 continue
             if rate['date'] <= now and rate['date'] > closer:
@@ -132,26 +151,27 @@
                 closer = rate['date']
         return res
 
-    def get_rate(self, ids, name):
+    @staticmethod
+    def get_rate(currencies, name):
         '''
         Return the rate at the date from the context or the current date
         '''
-        rate_obj = Pool().get('currency.currency.rate')
-        date_obj = Pool().get('ir.date')
+        Rate = Pool().get('currency.currency.rate')
+        Date = Pool().get('ir.date')
 
         res = {}
-        date = Transaction().context.get('date', date_obj.today())
-        for currency_id in ids:
-            rate_ids = rate_obj.search([
-                ('currency', '=', currency_id),
-                ('date', '<=', date),
-                ], limit=1, order=[('date', 'DESC')])
-            if rate_ids:
-                res[currency_id] = rate_ids[0]
+        date = Transaction().context.get('date', Date.today())
+        for currency in currencies:
+            rates = Rate.search([
+                    ('currency', '=', currency.id),
+                    ('date', '<=', date),
+                    ], limit=1, order=[('date', 'DESC')])
+            if rates:
+                res[currency.id] = rates[0].id
             else:
-                res[currency_id] = 0.0
+                res[currency.id] = 0.0
         rate_ids = [x for x in res.values() if x]
-        rates = rate_obj.browse(rate_ids)
+        rates = Rate.browse(rate_ids)
         id2rate = {}
         for rate in rates:
             id2rate[rate.id] = rate
@@ -160,7 +180,7 @@
                 res[currency_id] = id2rate[res[currency_id]].rate
         return res
 
-    def round(self, currency, amount, rounding=ROUND_HALF_EVEN):
+    def round(self, amount, rounding=ROUND_HALF_EVEN):
         '''
         Round the amount depending of the currency
 
@@ -171,80 +191,76 @@
         :param rounding: the rounding option
         :return: a Decimal
         '''
-        return (amount / currency.rounding).quantize(Decimal('1.'),
-                rounding=rounding) * currency.rounding
+        return (amount / self.rounding).quantize(Decimal('1.'),
+                rounding=rounding) * self.rounding
 
-    def is_zero(self, currency, amount):
+    def is_zero(self, amount):
         'Return True if the amount can be considered as zero for the currency'
-        return abs(self.round(currency, amount)) < currency.rounding
+        return abs(self.round(amount)) < self.rounding
 
-    def compute(self, from_currency, amount, to_currency, round=True):
+    @classmethod
+    def compute(cls, from_currency, amount, to_currency, round=True):
         '''
         Take a currency and an amount
         Return the amount to the new currency
         Use the rate of the date of the context or the current date if ids are
         given
         '''
-        date_obj = Pool().get('ir.date')
-        lang_obj = Pool().get('ir.lang')
+        Date = Pool().get('ir.date')
+        Lang = Pool().get('ir.lang')
+        from_currency = cls(from_currency.id)
+        to_currency = cls(to_currency.id)
 
-        if isinstance(from_currency, (int, long)):
-            from_currency = self.browse(from_currency)
-        if isinstance(to_currency, (int, long)):
-            to_currency = self.browse(to_currency)
         if to_currency == from_currency:
             if round:
-                return self.round(to_currency, amount)
+                return to_currency.round(amount)
             else:
                 return amount
         if (not from_currency.rate) or (not to_currency.rate):
-            date = Transaction().context.get('date', date_obj.today())
+            date = Transaction().context.get('date', Date.today())
             if not from_currency.rate:
                 name = from_currency.name
             else:
                 name = to_currency.name
 
-            lang_id, = lang_obj.search([
+            lang, = Lang.search([
                     ('code', '=', Transaction().language),
                     ])
-            lang = lang_obj.browse(lang_id)
 
-            self.raise_user_error('no_rate', (name,
-                datetime_strftime(date, str(lang.date))))
+            cls.raise_user_error('no_rate', (name,
+                    datetime_strftime(date, str(lang.date))))
         if round:
-            return self.round(to_currency,
-                    amount * to_currency.rate / from_currency.rate)
+            return to_currency.round(
+                amount * to_currency.rate / from_currency.rate)
         else:
             return amount * to_currency.rate / from_currency.rate
 
-Currency()
-
 
 class Rate(ModelSQL, ModelView):
     'Rate'
-    _name = 'currency.currency.rate'
-    _description = __doc__
+    __name__ = 'currency.currency.rate'
     _rec_name = 'date'
     date = fields.Date('Date', required=True, select=True)
     rate = fields.Numeric('Rate', digits=(12, 6), required=1)
     currency = fields.Many2One('currency.currency', 'Currency',
             ondelete='CASCADE',)
 
-    def __init__(self):
-        super(Rate, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Rate, cls).__setup__()
+        cls._sql_constraints = [
             ('date_currency_uniq', 'UNIQUE(date, currency)',
                 'A currency can only have one rate by date!'),
             ('check_currency_rate', 'CHECK(rate >= 0)',
                 'The currency rate must greater than or equal to 0'),
-        ]
-        self._order.insert(0, ('date', 'DESC'))
+            ]
+        cls._order.insert(0, ('date', 'DESC'))
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
-    def check_xml_record(self, ids, values):
+    @classmethod
+    def check_xml_record(self, records, values):
         return True
-
-Rate()
diff -r c9e235179e4d trytond/modules/currency/tests/test_currency.py
--- a/trytond/modules/currency/tests/test_currency.py	Fri Jun 08 19:33:58 2012 +0200
+++ b/trytond/modules/currency/tests/test_currency.py	Mon Sep 24 20:36:40 2012 +0200
@@ -51,19 +51,19 @@
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            cu1_id = self.currency.create({
-                'name': 'cu1',
-                'symbol': 'cu1',
-                'code': 'cu1'
-                })
-            self.assert_(cu1_id)
+            cu1 = self.currency.create({
+                    'name': 'cu1',
+                    'symbol': 'cu1',
+                    'code': 'cu1'
+                    })
+            self.assert_(cu1)
 
-            cu2_id = self.currency.create({
-                'name': 'cu2',
-                'symbol': 'cu2',
-                'code': 'cu2'
-                })
-            self.assert_(cu2_id)
+            cu2 = self.currency.create({
+                    'name': 'cu2',
+                    'symbol': 'cu2',
+                    'code': 'cu2'
+                    })
+            self.assert_(cu2)
 
             transaction.cursor.commit()
 
@@ -72,18 +72,18 @@
         Check grouping
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            cu1_id = self.get_currency('cu1')
+            cu1 = self.get_currency('cu1')
 
-            self.assertRaises(Exception, self.currency.write, cu1_id,
+            self.assertRaises(Exception, self.currency.write, [cu1],
                 {'mon_grouping': ''})
 
-            self.assertRaises(Exception, self.currency.write, cu1_id,
+            self.assertRaises(Exception, self.currency.write, [cu1],
                 {'mon_grouping': '[a]'})
 
-            self.assertRaises(Exception, self.currency.write, cu1_id,
+            self.assertRaises(Exception, self.currency.write, [cu1],
                 {'mon_grouping': '[1,"a"]'})
 
-            self.assertRaises(Exception, self.currency.write, cu1_id,
+            self.assertRaises(Exception, self.currency.write, [cu1],
                 {'mon_grouping': '[1,"1"]'})
 
     def test0030rate(self):
@@ -92,24 +92,22 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            cu1_id = self.get_currency('cu1')
-            cu2_id = self.get_currency('cu2')
+            cu1 = self.get_currency('cu1')
+            cu2 = self.get_currency('cu2')
 
-            rate1 = Decimal("1.3")
-            rate1_id = self.rate.create({
-                'rate': rate1,
-                'currency': cu1_id,
-                })
-            self.assert_(rate1_id)
+            rate1 = self.rate.create({
+                    'rate': Decimal("1.3"),
+                    'currency': cu1.id,
+                    })
+            self.assert_(rate1)
 
-            rate2_id = self.rate.create({
-                'rate': Decimal("1"),
-                'currency': cu2_id,
-                })
-            self.assert_(rate2_id)
+            rate2 = self.rate.create({
+                    'rate': Decimal("1"),
+                    'currency': cu2.id,
+                    })
+            self.assert_(rate2)
 
-            self.assertEqual(rate1, self.currency.read(cu1_id,
-                ['rate'])['rate'])
+            self.assertEqual(cu1.rate, Decimal("1.3"))
 
             transaction.cursor.commit()
 
@@ -121,21 +119,21 @@
                 context=CONTEXT) as transaction:
             today = self.date.today()
 
-            cu_id = self.currency.create({
-                'name': 'cu',
-                'symbol': 'cu',
-                'code': 'cu'
-                })
+            cu = self.currency.create({
+                    'name': 'cu',
+                    'symbol': 'cu',
+                    'code': 'cu'
+                    })
 
             self.rate.create({
-                'rate': Decimal("1.3"),
-                'currency': cu_id,
-                'date': today,
-                })
+                    'rate': Decimal("1.3"),
+                    'currency': cu.id,
+                    'date': today,
+                    })
 
             self.assertRaises(Exception, self.rate.create, {
                     'rate': Decimal("1.3"),
-                    'currency': cu_id,
+                    'currency': cu.id,
                     'date': today,
                     })
 
@@ -147,13 +145,13 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            cu1_id = self.get_currency('cu1')
-            cu2_id = self.get_currency('cu2')
+            cu1 = self.get_currency('cu1')
+            cu2 = self.get_currency('cu2')
 
             amount = Decimal("10")
             expected = Decimal("13")
             converted_amount = self.currency.compute(
-                    cu2_id, amount, cu1_id, True)
+                cu2, amount, cu1, True)
             self.assertEqual(converted_amount, expected)
 
             transaction.cursor.commit()
@@ -163,53 +161,53 @@
         Conversion with rounding on non-finite decimal representation
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            cu1_id = self.get_currency('cu1')
-            cu2_id = self.get_currency('cu2')
+            cu1 = self.get_currency('cu1')
+            cu2 = self.get_currency('cu2')
 
             amount = Decimal("10")
             expected = Decimal("7.69")
             converted_amount = self.currency.compute(
-                    cu1_id, amount, cu2_id, True)
-            self.assert_(converted_amount == expected)
+                cu1, amount, cu2, True)
+            self.assertEqual(converted_amount, expected)
 
     def test0070compute_nonfinite_worounding(self):
         '''
         Same without rounding
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            cu1_id = self.get_currency('cu1')
-            cu2_id = self.get_currency('cu2')
+            cu1 = self.get_currency('cu1')
+            cu2 = self.get_currency('cu2')
 
             amount = Decimal("10")
             expected = Decimal('7.692307692307692307692307692')
             converted_amount = self.currency.compute(
-                    cu1_id, amount, cu2_id, False)
-            self.assert_(converted_amount == expected)
+                cu1, amount, cu2, False)
+            self.assertEqual(converted_amount, expected)
 
     def test0080compute_same(self):
         '''
         Conversion to the same currency
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            cu1_id = self.get_currency('cu1')
+            cu1 = self.get_currency('cu1')
 
             amount = Decimal("10")
             converted_amount = self.currency.compute(
-                    cu1_id, amount, cu1_id, True)
-            self.assert_(converted_amount == amount)
+                cu1, amount, cu1, True)
+            self.assertEqual(converted_amount, amount)
 
     def test0090compute_zeroamount(self):
         '''
         Conversion with zero amount
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            cu1_id = self.get_currency('cu1')
-            cu2_id = self.get_currency('cu2')
+            cu1 = self.get_currency('cu1')
+            cu2 = self.get_currency('cu2')
 
             expected = Decimal("0")
             converted_amount = self.currency.compute(
-                    cu1_id, Decimal("0"), cu2_id, True)
-            self.assert_(converted_amount == expected)
+                cu1, Decimal("0"), cu2, True)
+            self.assertEqual(converted_amount, expected)
 
     def test0100compute_zerorate(self):
         '''
@@ -217,20 +215,20 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            cu1_id = self.get_currency('cu1')
-            cu2_id = self.get_currency('cu2')
+            cu1 = self.get_currency('cu1')
+            cu2 = self.get_currency('cu2')
 
-            rate_ids = self.rate.search([
-                    ('currency', '=', cu1_id),
+            rates = self.rate.search([
+                    ('currency', '=', cu1.id),
                     ], 0, 1, None)
-            self.rate.write(rate_ids, {
+            self.rate.write(rates, {
                     'rate': Decimal("0"),
                     })
             amount = Decimal("10")
             self.assertRaises(Exception, self.currency.compute,
-                cu1_id, amount, cu2_id, True)
+                cu1, amount, cu2, True)
             self.assertRaises(Exception, self.currency.compute,
-                    cu2_id, amount, cu1_id, True)
+                cu2, amount, cu1, True)
 
             transaction.cursor.rollback()
 
@@ -240,18 +238,18 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            cu1_id = self.get_currency('cu1')
-            cu3_id = self.currency.create({
-                'name': 'cu3',
-                'symbol': 'cu3',
-                'code': 'cu3'
-                })
+            cu1 = self.get_currency('cu1')
+            cu3 = self.currency.create({
+                    'name': 'cu3',
+                    'symbol': 'cu3',
+                    'code': 'cu3'
+                    })
 
             amount = Decimal("10")
             self.assertRaises(Exception, self.currency.compute,
-                    cu3_id, amount, cu1_id, True)
+                cu3, amount, cu1, True)
             self.assertRaises(Exception, self.currency.compute,
-                    cu1_id, amount, cu3_id, True)
+                cu1, amount, cu3, True)
 
             transaction.cursor.rollback()
 
@@ -261,20 +259,20 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            cu3_id = self.currency.create({
-                'name': 'cu3',
-                'symbol': 'cu3',
-                'code': 'cu3'
-                })
-            cu4_id = self.currency.create({
-                'name': 'cu4',
-                'symbol': 'cu4',
-                'code': 'cu4'
-                })
+            cu3 = self.currency.create({
+                    'name': 'cu3',
+                    'symbol': 'cu3',
+                    'code': 'cu3'
+                    })
+            cu4 = self.currency.create({
+                    'name': 'cu4',
+                    'symbol': 'cu4',
+                    'code': 'cu4'
+                    })
 
             amount = Decimal("10")
             self.assertRaises(Exception, self.currency.compute,
-                    cu3_id, amount, cu4_id, True)
+                cu3, amount, cu4, True)
 
             transaction.cursor.rollback()
 
@@ -285,13 +283,13 @@
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             codes = ['cu%s' % (i + 1) for i in range(2)]
-            currency_ids = [self.get_currency(i) for i in codes]
-            self.currency.delete(currency_ids)
+            currencies = [self.get_currency(i) for i in codes]
+            self.currency.delete(currencies)
 
-            rate_ids = self.rate.search([(
-                    'currency', 'in', currency_ids,
-                    )], 0, None, None)
-            self.assertFalse(rate_ids)
+            rates = self.rate.search([(
+                        'currency', 'in', map(int, currencies),
+                        )], 0, None, None)
+            self.assertFalse(rates)
 
             transaction.cursor.rollback()
 
diff -r 4994bdb47bfd trytond/modules/dashboard/__init__.py
--- a/trytond/modules/dashboard/__init__.py	Fri Jun 08 19:34:00 2012 +0200
+++ b/trytond/modules/dashboard/__init__.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,6 +1,15 @@
 #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.pool import Pool
 from .dashboard import *
 from .res import *
 from .ir import *
+
+
+def register():
+    Pool.register(
+        DashboardAction,
+        User,
+        View,
+        module='dashboard', type_='model')
diff -r 4994bdb47bfd trytond/modules/dashboard/dashboard.py
--- a/trytond/modules/dashboard/dashboard.py	Fri Jun 08 19:34:00 2012 +0200
+++ b/trytond/modules/dashboard/dashboard.py	Mon Sep 24 20:36:40 2012 +0200
@@ -4,11 +4,12 @@
 from trytond.transaction import Transaction
 from trytond.backend import TableHandler
 
+__all__ = ['DashboardAction']
+
 
 class DashboardAction(ModelSQL, ModelView):
     "Dashboard Action"
-    _name = "dashboard.action"
-
+    __name__ = "dashboard.action"
     user = fields.Many2One('res.user', 'User', required=True,
             select=True)
     sequence = fields.Integer('Sequence',
@@ -20,17 +21,17 @@
                 ('res_model', '!=', ''),
             ])
 
-    def __init__(self):
-        super(DashboardAction, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(DashboardAction, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(DashboardAction, self).init(module_name)
+        super(DashboardAction, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
-
-DashboardAction()
diff -r 4994bdb47bfd trytond/modules/dashboard/ir.py
--- a/trytond/modules/dashboard/ir.py	Fri Jun 08 19:34:00 2012 +0200
+++ b/trytond/modules/dashboard/ir.py	Mon Sep 24 20:36:40 2012 +0200
@@ -2,67 +2,62 @@
 #this repository contains the full copyright notices and license terms.
 import math
 from lxml import etree
-from trytond.model import Model
 from trytond.transaction import Transaction
 from trytond.cache import Cache
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['View']
+__metaclass__ = PoolMeta
 
-class View(Model):
-    _name = 'ir.ui.view'
 
-    def dashboard_id(self):
+class View:
+    __name__ = 'ir.ui.view'
+    _dashboard_cache = Cache('ir.ui.view.dashboard_id')
+
+    @staticmethod
+    def dashboard_id():
         '''
         Return the database id of view_dashboard
-
-        :param user: the user id
-        :return: an integer
         '''
-        model_data_obj = Pool().get('ir.model.data')
-        model_data_ids = model_data_obj.search([
-            ('fs_id', '=', 'view_dashboard'),
-            ('module', '=', 'dashboard'),
-            ('inherit', '=', None),
-            ], limit=1)
-        if not model_data_ids:
+        ModelData = Pool().get('ir.model.data')
+        models_data = ModelData.search([
+                ('fs_id', '=', 'view_dashboard'),
+                ('module', '=', 'dashboard'),
+                ('inherit', '=', None),
+                ], limit=1)
+        if not models_data:
             return 0
-        model_data = model_data_obj.browse(model_data_ids[0])
+        model_data, = models_data
         return model_data.db_id
 
-    dashboard_id = Cache('ir.ui.view.dashboard_id')(dashboard_id)
-
-    def _dashboard_element_action(self, action):
+    @staticmethod
+    def _dashboard_element_action(action):
         '''
         Return etree Element for the given dashboard action.
-
-        :param action: a BrowseRecord of dashboard.action
-        :return: an etree Element
         '''
         return etree.Element('action', {
-            'name': str(action.act_window.id),
-            })
+                'name': str(action.act_window.id),
+                })
 
-    def dashboard_view(self, arch):
+    @classmethod
+    def dashboard_view(cls, arch):
         '''
         Add action to view arch of dashboard
-
-        :param arch: a string with the xml arch of dashboard
-        :return: a string with the new xml arch
         '''
-        user_obj = Pool().get('res.user')
+        User = Pool().get('res.user')
         tree = etree.fromstring(arch)
         root = tree.getroottree().getroot()
-        user = user_obj.browse(Transaction().user)
+        user = User(Transaction().user)
         if user.dashboard_layout == 'square':
             root.set('col', str(int(math.ceil(math.sqrt(
                 len(user.dashboard_actions))))))
             for action in user.dashboard_actions:
-                root.append(self._dashboard_element_action(action))
+                root.append(cls._dashboard_element_action(action))
         elif user.dashboard_layout == 'stack_right':
             group = None
             root.set('col', '2')
             for action in user.dashboard_actions:
-                element = self._dashboard_element_action(action)
+                element = cls._dashboard_element_action(action)
                 if group is None:
                     root.append(element)
                     group = etree.Element('group', {
@@ -83,7 +78,7 @@
             root.append(group)
             first = True
             for action in user.dashboard_actions:
-                element = self._dashboard_element_action(action)
+                element = cls._dashboard_element_action(action)
                 if first:
                     first = False
                     root.append(element)
@@ -98,7 +93,7 @@
             root.append(group)
             first = True
             for action in user.dashboard_actions:
-                element = self._dashboard_element_action(action)
+                element = cls._dashboard_element_action(action)
                 if first:
                     first = False
                     root.append(element)
@@ -112,7 +107,7 @@
                     })
             first = True
             for action in user.dashboard_actions:
-                element = self._dashboard_element_action(action)
+                element = cls._dashboard_element_action(action)
                 if first:
                     first = False
                     root.append(element)
@@ -122,23 +117,19 @@
         arch = etree.tostring(tree, encoding='utf-8')
         return arch
 
-    def read(self, ids, fields_names=None):
-        res = super(View, self).read(ids, fields_names=fields_names)
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        res = super(View, cls).read(ids, fields_names=fields_names)
         if Transaction().user == 0:
             return res
-        dashboard_id = self.dashboard_id()
+        dashboard_id = cls.dashboard_id()
         if not dashboard_id:
             # Restart the cache
-            self.dashboard_id.reset()
+            cls._dashboard_cache.reset()
         if fields_names is None \
                 or 'arch' in fields_names:
-            if isinstance(ids, (int, long)):
-                if dashboard_id == ids:
-                    res['arch'] = self.dashboard_view(res['arch'])
-            elif dashboard_id in ids:
+            if dashboard_id in ids:
                 for res2 in res:
                     if res2['id'] == dashboard_id:
-                        res2['arch'] = self.dashboard_view(res2['arch'])
+                        res2['arch'] = cls.dashboard_view(res2['arch'])
         return res
-
-View()
diff -r 4994bdb47bfd trytond/modules/dashboard/res.py
--- a/trytond/modules/dashboard/res.py	Fri Jun 08 19:34:00 2012 +0200
+++ b/trytond/modules/dashboard/res.py	Mon Sep 24 20:36:40 2012 +0200
@@ -1,13 +1,16 @@
 #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 Model, fields
+from trytond.model import fields
 from trytond.backend import TableHandler
 from trytond.transaction import Transaction
+from trytond.pool import PoolMeta
 
+__all__ = ['User']
+__metaclass__ = PoolMeta
 
-class User(Model):
-    _name = "res.user"
 
+class User:
+    __name__ = "res.user"
     dashboard_layout = fields.Selection([
         ('square', 'Square'),
         ('stack_right', 'Stack Right'),
@@ -18,22 +21,23 @@
     dashboard_actions = fields.One2Many('dashboard.action', 'user',
             'Dashboard Actions')
 
-    def __init__(self):
-        super(User, self).__init__()
-        self._preferences_fields += [
+    @classmethod
+    def __setup__(cls):
+        super(User, cls).__setup__()
+        cls._preferences_fields += [
             'dashboard_layout',
             'dashboard_actions',
-        ]
+            ]
 
-    def init(self, module_name):
-        super(User, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(User, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.6
         table.not_null_action('dashboard_layout', action='remove')
 
-    def default_dashboard_layout(self):
+    @staticmethod
+    def default_dashboard_layout():
         return 'square'
-
-User()
diff -r 52a09f859b60 trytond/modules/google_maps/__init__.py
--- a/trytond/modules/google_maps/__init__.py	Fri Jun 08 19:34:01 2012 +0200
+++ b/trytond/modules/google_maps/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,4 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .address import *
+
+
+def register():
+    Pool.register(
+        Address,
+        module='google_maps', type_='model')
diff -r 52a09f859b60 trytond/modules/google_maps/address.py
--- a/trytond/modules/google_maps/address.py	Fri Jun 08 19:34:01 2012 +0200
+++ b/trytond/modules/google_maps/address.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,64 +1,38 @@
 #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 urllib
-from trytond.model import ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import PoolMeta
 
+__all__ = ['Address']
+__metaclass__ = PoolMeta
 
-class Address(ModelSQL, ModelView):
-    _name = 'party.address'
+
+class Address:
+    __name__ = 'party.address'
 
     google_maps_url = fields.Function(fields.Char('Google Maps',
-        on_change_with=['street', 'streetbis', 'zip', 'city', 'country',
-            'subdivision']), 'get_google_maps_url')
+            on_change_with=['street', 'streetbis', 'zip', 'city', 'country',
+                'subdivision']), 'on_change_with_google_maps_url')
 
-    def _get_url(self, vals):
+    def on_change_with_google_maps_url(self, name=None):
         lang = Transaction().language[:2]
         url = ''
-        for i in ['street', 'streetbis', 'zip', 'city',
-                'country', 'subdivision']:
-            if vals.get(i):
-                if isinstance(vals[i], str):
-                    url += ' ' + vals[i].decode('utf-8')
+        for value in (
+                self.street,
+                self.streetbis,
+                self.zip,
+                self.city,
+                self.country.name if self.country else None,
+                self.subdivision.name if self.subdivision else None,
+                ):
+            if value:
+                if isinstance(value, str):
+                    url += ' ' + value.decode('utf-8')
                 else:
-                    url += ' ' + vals[i]
+                    url += ' ' + value
         if url.strip():
-            url = 'http://maps.google.com/maps?hl=%s&q=%s' % \
-                    (lang, urllib.quote(url.strip().encode('utf-8')))
-        else:
-            url = ''
-        return url
-
-    def on_change_with_google_maps_url(self, vals):
-        country_obj = Pool().get('country.country')
-        subdivision_obj = Pool().get('country.subdivision')
-
-        vals = vals.copy()
-
-        if vals.get('country'):
-            country = country_obj.browse(vals['country'])
-            vals['country'] = country.name
-
-        if vals.get('subdivision'):
-            subdivision = subdivision_obj.browse(vals['subdivision'])
-            vals['subdivision'] = subdivision.name
-
-        return self._get_url(vals)
-
-    def get_google_maps_url(self, ids, name):
-        res = {}
-        for address in self.browse(ids):
-            vals = {
-                'street': address.street,
-                'streetbis': address.streetbis,
-                'zip': address.zip,
-                'city': address.city,
-                'country': address.country and address.country.name or None,
-                'subdivision': address.subdivision and \
-                        address.subdivision.name or None,
-            }
-            res[address.id] = self._get_url(vals)
-        return res
-
-Address()
+            return 'http://maps.google.com/maps?hl=%s&q=%s' % \
+                (lang, urllib.quote(url.strip().encode('utf-8')))
+        return ''
diff -r 8eedb253678d trytond/modules/ldap_authentication/__init__.py
--- a/trytond/modules/ldap_authentication/__init__.py	Fri Jun 08 19:34:02 2012 +0200
+++ b/trytond/modules/ldap_authentication/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,5 +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.
 
+from trytond.pool import Pool
 from .connection import *
 from .res import *
+
+
+def register():
+    Pool.register(
+        Connection,
+        User,
+        module='ldap_authentication', type_='model')
diff -r 8eedb253678d trytond/modules/ldap_authentication/connection.py
--- a/trytond/modules/ldap_authentication/connection.py	Fri Jun 08 19:34:02 2012 +0200
+++ b/trytond/modules/ldap_authentication/connection.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,10 +1,14 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
+from trytond.model import fields
+from trytond.pool import PoolMeta
 
+__all__ = ['Connection']
+__metaclass__ = PoolMeta
 
-class Connection(ModelSingleton, ModelSQL, ModelView):
-    _name = 'ldap.connection'
+
+class Connection:
+    __name__ = 'ldap.connection'
 
     auth_uid = fields.Char('UID', help='UID Attribute for authentication',
             required=True)
@@ -19,13 +23,14 @@
     auth_create_user = fields.Boolean('Create User',
             help='Create user if not in database')
 
-    def default_auth_uid(self):
+    @staticmethod
+    def default_auth_uid():
         return 'uid'
 
-    def default_auth_scope(self):
+    @staticmethod
+    def default_auth_scope():
         return 'base'
 
-    def default_auth_create_user(self):
+    @staticmethod
+    def default_auth_create_user():
         return False
-
-Connection()
diff -r 8eedb253678d trytond/modules/ldap_authentication/res.py
--- a/trytond/modules/ldap_authentication/res.py	Fri Jun 08 19:34:02 2012 +0200
+++ b/trytond/modules/ldap_authentication/res.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,30 +1,30 @@
 #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 ldap
-from trytond.model import ModelView, ModelSQL
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['User']
+__metaclass__ = PoolMeta
 
-class User(ModelSQL, ModelView):
-    _name = 'res.user'
 
-    def __init__(self):
-        super(User, self).__init__()
-        self._error_messages.update({
-            'set_passwd_ldap_user': \
+class User:
+    __name__ = 'res.user'
+
+    @classmethod
+    def __setup__(cls):
+        super(User, cls).__setup__()
+        cls._error_messages.update({
+                'set_passwd_ldap_user': \
                     'You can not set a password to a ldap user!',
-            })
+                })
 
-    def ldap_search_user(self, login, con, connection, attrs=None):
+    @staticmethod
+    def ldap_search_user(login, con, connection, attrs=None):
         '''
-        Return the resule of a ldap search for the login
-
-        :param login: the login
-        :param con: the ldap connection
-        :param connection: a BrowseRecord of ldap.connection
-        :param attrs: a list of attribute to return
-        :return: the ldap search result
+        Return the result of a ldap search for the login using the ldap
+        connection con based on connection.
+        The attributes values defined in attrs will be return.
         '''
         scope = {
             'base': ldap.SCOPE_BASE,
@@ -43,11 +43,11 @@
             result = [x for x in result if x[0]]
         return result
 
-    def _check_passwd_ldap_user(self, logins):
-        connection_obj = Pool().get('ldap.connection')
-        connection_ids = connection_obj.search([], limit=1)
+    @classmethod
+    def _check_passwd_ldap_user(cls, logins):
+        Connection = Pool().get('ldap.connection')
         with Transaction().set_user(0):
-            connection = connection_obj.browse(connection_ids[0])
+            connection, = Connection.search([], limit=1)
         find = False
         try:
             con = ldap.initialize(connection.uri)
@@ -58,35 +58,33 @@
             if connection.bind_dn:
                 con.simple_bind_s(connection.bind_dn, connection.bind_pass)
             for login in logins:
-                if self.ldap_search_user(login,
+                if cls.ldap_search_user(login,
                         con, connection, attrs=[]):
                     find = True
         except Exception:
             pass
         if find:
-            self.raise_user_error('set_passwd_ldap_user')
+            cls.raise_user_error('set_passwd_ldap_user')
 
-    def create(self, vals):
-        if vals.get('password') and 'login' in vals:
-            self._check_passwd_ldap_user([vals['login']])
-        return super(User, self).create(vals)
+    @classmethod
+    def create(cls, values):
+        if values.get('password') and 'login' in values:
+            cls._check_passwd_ldap_user([values['login']])
+        return super(User, cls).create(values)
 
-    def write(self, ids, vals):
-        if vals.get('password'):
-            if isinstance(ids, (int, long)):
-                ids2 = [ids]
-            else:
-                ids2 = ids
-            logins = [x.login for x in self.browse(ids2)]
-            self._check_passwd_ldap_user(logins)
-        return super(User, self).write(ids, vals)
+    @classmethod
+    def write(cls, users, values):
+        if values.get('password'):
+            logins = [x.login for x in users]
+            cls._check_passwd_ldap_user(logins)
+        super(User, cls).write(users, values)
 
-    def set_preferences(self, values, old_password=False):
-        connection_obj = Pool().get('ldap.connection')
+    @classmethod
+    def set_preferences(cls, values, old_password=False):
+        Connection = Pool().get('ldap.connection')
         if 'password' in values:
-            connection_ids = connection_obj.search([], limit=1)
             with Transaction().set_user(0):
-                connection = connection_obj.browse(connection_ids[0])
+                connection, = Connection.search([], limit=1)
             try:
                 con = ldap.initialize(connection.uri)
                 if connection.active_directory:
@@ -95,25 +93,24 @@
                     con.start_tls_s()
                 if connection.bind_dn:
                     con.simple_bind_s(connection.bind_dn, connection.bind_pass)
-                user = self.browse(Transaction().user)
-                [(dn, attrs)] = self.ldap_search_user(user.login, con,
-                        connection, attrs=[str(connection.auth_uid)])
+                user = cls(Transaction().user)
+                [(dn, attrs)] = cls.ldap_search_user(user.login, con,
+                    connection, attrs=[str(connection.auth_uid)])
                 if con.simple_bind_s(dn, old_password):
                     con.passwd_s(dn, old_password, values['password'])
                     values = values.copy()
                     del values['password']
                 else:
-                    self.raise_user_error('wrong_password')
+                    cls.raise_user_error('wrong_password')
             except Exception:
                 pass
-        return super(User, self).set_preferences(values,
-                old_password=old_password)
+        super(User, cls).set_preferences(values, old_password=old_password)
 
-    def get_login(self, login, password):
-        connection_obj = Pool().get('ldap.connection')
-        connection_ids = connection_obj.search([], limit=1)
+    @classmethod
+    def get_login(cls, login, password):
+        Connection = Pool().get('ldap.connection')
         with Transaction().set_user(0):
-            connection = connection_obj.browse(connection_ids[0])
+            connection, = Connection.search([], limit=1)
         try:
             con = ldap.initialize(connection.uri)
             if connection.active_directory:
@@ -122,21 +119,19 @@
                 con.start_tls_s()
             if connection.bind_dn:
                 con.simple_bind_s(connection.bind_dn, connection.bind_pass)
-            [(dn, attrs)] = self.ldap_search_user(login, con, connection,
-                    attrs=[str(connection.auth_uid)])
+            [(dn, attrs)] = cls.ldap_search_user(login, con, connection,
+                attrs=[str(connection.auth_uid)])
             if password and con.simple_bind_s(dn, password):
-                user_id, _, _ = self._get_login(login)
+                user_id, _, _ = cls._get_login(login)
                 if user_id:
                     return user_id
                 elif connection.auth_create_user:
-                    user_id = self.create({
-                        'name': attrs.get(str(connection.auth_uid),
+                    user = cls.create({
+                            'name': attrs.get(str(connection.auth_uid),
                                 [login])[0],
-                        'login': login,
-                        })
-                    return user_id
+                            'login': login,
+                            })
+                    return user.id
         except Exception:
             pass
-        return super(User, self).get_login(login, password)
-
-User()
+        return super(User, cls).get_login(login, password)
diff -r f4b665cac283 trytond/modules/ldap_connection/__init__.py
--- a/trytond/modules/ldap_connection/__init__.py	Mon Sep 10 15:28:10 2012 +0200
+++ b/trytond/modules/ldap_connection/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,4 +1,15 @@
 #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.pool import Pool
 from .connection import *
+
+
+def register():
+    Pool.register(
+        Connection,
+        TestConnectionResult,
+        module='ldap_connection', type_='model')
+    Pool.register(
+        TestConnection,
+        module='ldap_connection', type_='wizard')
diff -r f4b665cac283 trytond/modules/ldap_connection/connection.py
--- a/trytond/modules/ldap_connection/connection.py	Mon Sep 10 15:28:10 2012 +0200
+++ b/trytond/modules/ldap_connection/connection.py	Mon Sep 24 20:36:41 2012 +0200
@@ -7,13 +7,13 @@
 from trytond.pool import Pool
 from trytond.transaction import Transaction
 
+__all__ = ['Connection', 'TestConnectionResult', 'TestConnection']
+
 
 class Connection(ModelSingleton, ModelSQL, ModelView):
     "LDAP Connection"
-    _description = __doc__
-    _name = 'ldap.connection'
+    __name__ = 'ldap.connection'
     _rec_name = 'server'
-
     server = fields.Char('Server', required=True, help='LDAP server name')
     port = fields.Integer('Port', required=True, help='LDAP server port')
     secure = fields.Selection([
@@ -31,74 +31,68 @@
     uri = fields.Function(fields.Char('URI'), 'get_uri')
     active_directory = fields.Boolean('Active Directory')
 
-    def __init__(self):
-        super(Connection, self).__init__()
-        self._buttons.update({
+    @classmethod
+    def __setup__(cls):
+        super(Connection, cls).__setup__()
+        cls._buttons.update({
                 'test_connection': {},
                 })
 
-    def default_port(self):
+    @staticmethod
+    def default_port():
         return 389
 
-    def default_secure(self):
+    @staticmethod
+    def default_secure():
         return 'never'
 
-    def default_active_directory(self):
+    @staticmethod
+    def default_active_directory():
         return False
 
-    def on_change_secure(self, values):
+    def on_change_secure(self):
         res = {}
-        if values.get('secure') in ('never', 'tls'):
+        if self.secure in ('never', 'tls'):
             res['port'] = self.default_port()
-        elif values.get('secure') == 'ssl':
+        elif self.secure == 'ssl':
             res['port'] = 636
         return res
 
-    def get_uri(self, ids, name):
-        res = {}
-        for connection in self.browse(ids):
-            res[connection.id] = \
-                    (connection.secure == 'ssl' and 'ldaps' or 'ldap') + \
-                    '://%s:%s/' % (connection.server, connection.port)
-        return res
+    def get_uri(self, name):
+        return ((self.secure == 'ssl' and 'ldaps' or 'ldap') +
+            '://%s:%s/' % (self.server, self.port))
 
+    @classmethod
     @ModelView.button_action('ldap_connection.wizard_test_connection')
-    def test_connection(self, ids):
+    def test_connection(cls, connections):
         pass
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, connections, values):
         if 'bind_dn' in values and not values['bind_dn']:
             values = values.copy()
             values['bind_pass'] = None
-        return super(Connection, self).write(ids, values)
-
-Connection()
+        return super(Connection, cls).write(connections, values)
 
 
 class TestConnectionResult(ModelView):
     'Test Connection'
-    _description = __doc__
-    _name = 'ldap.test_connection.result'
-
-TestConnectionResult()
+    __name__ = 'ldap.test_connection.result'
 
 
 class TestConnection(Wizard):
     "Test LDAP Connection"
-    _description = __doc__
-    _name = 'ldap.test_connection'
+    __name__ = 'ldap.test_connection'
     start_state = 'test'
-
     test = StateTransition()
     result = StateView('ldap.test_connection.result',
         'ldap_connection.test_connection_result_form', [
             Button('Close', 'end', 'tryton-close'),
             ])
 
-    def transition_test(self, session):
-        connection_obj = Pool().get('ldap.connection')
-        connection_id = Transaction().context.get('active_id')
-        connection = connection_obj.browse(connection_id)
+    def transition_test(self):
+        Connection = Pool().get('ldap.connection')
+        connection = Connection(Transaction().context.get('active_id'))
         con = ldap.initialize(connection.uri)
         if connection.secure == 'tls':
             con.start_tls_s()
@@ -107,5 +101,3 @@
         else:
             con.simple_bind_s()
         return 'result'
-
-TestConnection()
diff -r f0d5fbea32c2 trytond/modules/party/__init__.py
--- a/trytond/modules/party/__init__.py	Wed Sep 19 22:20:38 2012 +0200
+++ b/trytond/modules/party/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,8 +1,25 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .category import *
 from .party import *
 from .address import *
 from .contact_mechanism import *
 from .configuration import *
+
+
+def register():
+    Pool.register(
+        Category,
+        Party,
+        PartyCategory,
+        CheckVIESResult,
+        CheckVIESNoResult,
+        Address,
+        ContactMechanism,
+        Configuration,
+        module='party', type_='model')
+    Pool.register(
+        CheckVIES,
+        module='party', type_='wizard')
diff -r f0d5fbea32c2 trytond/modules/party/address.py
--- a/trytond/modules/party/address.py	Wed Sep 19 22:20:38 2012 +0200
+++ b/trytond/modules/party/address.py	Mon Sep 24 20:36:41 2012 +0200
@@ -3,10 +3,11 @@
 'Address'
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.pyson import Eval, If
-from trytond.pool import Pool
 from trytond.transaction import Transaction
 from trytond.backend import TableHandler
 
+__all__ = ['Address']
+
 STATES = {
     'readonly': ~Eval('active'),
     }
@@ -15,10 +16,9 @@
 
 class Address(ModelSQL, ModelView):
     "Address"
-    _name = 'party.address'
-    _description = __doc__
+    __name__ = 'party.address'
     party = fields.Many2One('party.party', 'Party', required=True,
-        ondelete='CASCADE', select=True,  states={
+        ondelete='CASCADE', select=True, states={
             'readonly': If(~Eval('active'), True, Eval('id', 0) > 0),
             },
         depends=['active', 'id'])
@@ -39,98 +39,86 @@
     full_address = fields.Function(fields.Text('Full Address'),
             'get_full_address')
 
-    def __init__(self):
-        super(Address, self).__init__()
-        self._order.insert(0, ('party', 'ASC'))
-        self._order.insert(1, ('sequence', 'ASC'))
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Address, cls).__setup__()
+        cls._order.insert(0, ('party', 'ASC'))
+        cls._order.insert(1, ('sequence', 'ASC'))
+        cls._error_messages.update({
             'write_party': 'You can not modify the party of an address!',
             })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(Address, self).init(module_name)
+        super(Address, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def get_full_address(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for address in self.browse(ids):
-            res[address.id] = ''
-            if address.name:
-                res[address.id] += address.name
-            if address.street:
-                if res[address.id]:
-                    res[address.id] += '\n'
-                res[address.id] += address.street
-            if address.streetbis:
-                if res[address.id]:
-                    res[address.id] += '\n'
-                res[address.id] += address.streetbis
-            if address.zip or address.city:
-                if res[address.id]:
-                    res[address.id] += '\n'
-                if address.zip:
-                    res[address.id] += address.zip
-                if address.city:
-                    if res[address.id][-1:] != '\n':
-                        res[address.id] += ' '
-                    res[address.id] += address.city
-            if address.country or address.subdivision:
-                if res[address.id]:
-                    res[address.id] += '\n'
-                if address.subdivision:
-                    res[address.id] += address.subdivision.name
-                if address.country:
-                    if res[address.id][-1:] != '\n':
-                        res[address.id] += ' '
-                    res[address.id] += address.country.name
-        return res
+    def get_full_address(self, name):
+        full_address = ''
+        if self.name:
+            full_address = self.name
+        if self.street:
+            if full_address:
+                full_address += '\n'
+            full_address += self.street
+        if self.streetbis:
+            if full_address:
+                full_address += '\n'
+            full_address += self.streetbis
+        if self.zip or self.city:
+            if full_address:
+                full_address += '\n'
+            if self.zip:
+                full_address += self.zip
+            if self.city:
+                if full_address[-1:] != '\n':
+                    full_address += ' '
+                full_address += self.city
+        if self.country or self.subdivision:
+            if full_address:
+                full_address += '\n'
+            if self.subdivision:
+                full_address += self.subdivision.name
+            if self.country:
+                if full_address[-1:] != '\n':
+                    full_address += ' '
+                full_address += self.country.name
+        return full_address
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for address in self.browse(ids):
-            res[address.id] = ", ".join(x for x in [address.name,
-                address.party.rec_name, address.zip, address.city] if x)
-        return res
+    def get_rec_name(self, name):
+        return ", ".join(x for x in [self.name,
+                self.party.rec_name, self.zip, self.city] if x)
 
-    def search_rec_name(self, name, clause):
-        ids = self.search(['OR',
-            ('zip',) + clause[1:],
-            ('city',) + clause[1:],
-            ('name',) + clause[1:],
-            ], order=[])
-        if ids:
-            return [('id', 'in', ids)]
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        addresses = cls.search(['OR',
+                ('zip',) + clause[1:],
+                ('city',) + clause[1:],
+                ('name',) + clause[1:],
+                ], order=[])
+        if addresses:
+            return [('id', 'in', [address.id for address in addresses])]
         return [('party',) + clause[1:]]
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, addresses, vals):
         if 'party' in vals:
-            if isinstance(ids, (int, long)):
-                ids = [ids]
-            for address in self.browse(ids):
+            for address in addresses:
                 if address.party.id != vals['party']:
-                    self.raise_user_error('write_party')
-        return super(Address, self).write(ids, vals)
+                    cls.raise_user_error('write_party')
+        super(Address, cls).write(addresses, vals)
 
-    def on_change_country(self, vals):
-        subdivision_obj = Pool().get('country.subdivision')
-        result = dict((k, vals.get(k))
-            for k in ('country', 'subdivision'))
-        if vals['subdivision']:
-            subdivision = subdivision_obj.browse(vals['subdivision'])
-            if subdivision.country.id != vals['country']:
-                result['subdivision'] = None
-        return result
-
-Address()
+    def on_change_country(self):
+        if (self.subdivision
+                and self.subdivision.country != self.country):
+            return {'subdivision': None}
+        return {}
diff -r f0d5fbea32c2 trytond/modules/party/category.py
--- a/trytond/modules/party/category.py	Wed Sep 19 22:20:38 2012 +0200
+++ b/trytond/modules/party/category.py	Mon Sep 24 20:36:41 2012 +0200
@@ -3,6 +3,8 @@
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.pyson import Eval
 
+__all__ = ['Category']
+
 STATES = {
     'readonly': ~Eval('active'),
 }
@@ -13,8 +15,7 @@
 
 class Category(ModelSQL, ModelView):
     "Category"
-    _name = "party.category"
-    _description = __doc__
+    __name__ = 'party.category'
     name = fields.Char('Name', required=True, states=STATES, translate=True,
         depends=DEPENDS)
     parent = fields.Many2One('party.category', 'Parent',
@@ -23,48 +24,39 @@
        'Children', states=STATES, depends=DEPENDS)
     active = fields.Boolean('Active')
 
-    def __init__(self):
-        super(Category, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Category, cls).__setup__()
+        cls._sql_constraints = [
             ('name_parent_uniq', 'UNIQUE(name, parent)',
                 'The name of a party category must be unique by parent!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_recursion', 'recursive_categories'),
             ('check_name', 'wrong_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_categories': 'You can not create recursive categories!',
             'wrong_name': 'You can not use "%s" in name field!' % SEPARATOR,
         })
-        self._order.insert(1, ('name', 'ASC'))
+        cls._order.insert(1, ('name', 'ASC'))
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def check_name(self, ids):
-        for category in self.browse(ids):
-            if SEPARATOR in category.name:
-                return False
+    def check_name(self):
+        if SEPARATOR in self.name:
+            return False
         return True
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.get_rec_name(name) + SEPARATOR + self.name
+        return self.name
 
-        def _name(category):
-            if category.id in res:
-                return res[category.id]
-            elif category.parent:
-                return _name(category.parent) + SEPARATOR + category.name
-            else:
-                return category.name
-        for category in self.browse(ids):
-            res[category.id] = _name(category)
-        return res
-
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         if isinstance(clause[2], basestring):
             values = clause[2].split(SEPARATOR)
             values.reverse()
@@ -73,9 +65,7 @@
             for name in values:
                 domain.append((field, clause[1], name))
                 field = 'parent.' + field
-            ids = self.search(domain, order=[])
-            return [('id', 'in', ids)]
+            categories = cls.search(domain, order=[])
+            return [('id', 'in', [category.id for category in categories])]
         #TODO Handle list
         return [('name',) + tuple(clause[1:])]
-
-Category()
diff -r f0d5fbea32c2 trytond/modules/party/configuration.py
--- a/trytond/modules/party/configuration.py	Wed Sep 19 22:20:38 2012 +0200
+++ b/trytond/modules/party/configuration.py	Mon Sep 24 20:36:41 2012 +0200
@@ -2,11 +2,12 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
 
+__all__ = ['Configuration']
+
 
 class Configuration(ModelSingleton, ModelSQL, ModelView):
     'Party Configuration'
-    _name = 'party.configuration'
-    _description = __doc__
+    __name__ = 'party.configuration'
 
     party_sequence = fields.Property(fields.Many2One('ir.sequence',
             'Party Sequence', domain=[
@@ -15,5 +16,3 @@
     party_lang = fields.Property(fields.Many2One("ir.lang", 'Party Language',
         help=('The value set on this field will preset the language on new '
             'parties')))
-
-Configuration()
diff -r f0d5fbea32c2 trytond/modules/party/contact_mechanism.py
--- a/trytond/modules/party/contact_mechanism.py	Wed Sep 19 22:20:38 2012 +0200
+++ b/trytond/modules/party/contact_mechanism.py	Mon Sep 24 20:36:41 2012 +0200
@@ -5,6 +5,8 @@
 from trytond.transaction import Transaction
 from trytond.backend import TableHandler
 
+__all__ = ['ContactMechanism']
+
 STATES = {
     'readonly': ~Eval('active'),
     }
@@ -26,8 +28,7 @@
 
 class ContactMechanism(ModelSQL, ModelView):
     "Contact Mechanism"
-    _name = "party.contact_mechanism"
-    _description = __doc__
+    __name__ = 'party.contact_mechanism'
     _rec_name = 'value'
 
     type = fields.Selection(_TYPES, 'Type', required=True, states=STATES,
@@ -72,41 +73,42 @@
         }, on_change=['other_value'], depends=['value', 'type', 'active']),
         'get_value', setter='set_value')
 
-    def __init__(self):
-        super(ContactMechanism, self).__init__()
-        self._order.insert(0, ('party', 'ASC'))
-        self._order.insert(1, ('sequence', 'ASC'))
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ContactMechanism, cls).__setup__()
+        cls._order.insert(0, ('party', 'ASC'))
+        cls._order.insert(1, ('sequence', 'ASC'))
+        cls._error_messages.update({
             'write_party': 'You can not modify the party of ' \
                     'a contact mechanism!',
             })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(ContactMechanism, self).init(module_name)
+        super(ContactMechanism, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'phone'
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def get_value(self, ids, names):
-        res = {}
-        for name in names:
-            res[name] = {}
-        for mechanism in self.browse(ids):
-            for name in names:
-                res[name][mechanism.id] = mechanism.value
-        return res
+    @classmethod
+    def get_value(cls, mechanisms, names):
+        return dict((name, dict((m.id, m.value) for m in mechanisms))
+            for name in names)
 
-    def set_value(self, ids, name, value):
-        self.write(ids, {
+    @classmethod
+    def set_value(cls, mechanisms, name, value):
+        cls.write(mechanisms, {
             'value': value,
             })
 
@@ -118,33 +120,30 @@
             'skype': value,
             'sip': value,
             'other_value': value,
-        }
+            }
 
-    def on_change_value(self, vals):
-        return self._change_value(vals.get('value'))
+    def on_change_value(self):
+        return self._change_value(self.value)
 
-    def on_change_website(self, vals):
-        return self._change_value(vals.get('website'))
+    def on_change_website(self):
+        return self._change_value(self.website)
 
-    def on_change_email(self, vals):
-        return self._change_value(vals.get('email'))
+    def on_change_email(self):
+        return self._change_value(self.email)
 
-    def on_change_skype(self, vals):
-        return self._change_value(vals.get('skype'))
+    def on_change_skype(self):
+        return self._change_value(self.skype)
 
-    def on_change_sip(self, vals):
-        return self._change_value(vals.get('sip'))
+    def on_change_sip(self):
+        return self._change_value(self.sip)
 
-    def on_change_other_value(self, vals):
-        return self._change_value(vals.get('other_value'))
+    def on_change_other_value(self):
+        return self._change_value(self.other_value)
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, mechanisms, vals):
         if 'party' in vals:
-            if isinstance(ids, (int, long)):
-                ids = [ids]
-            for mechanism in self.browse(ids):
+            for mechanism in mechanisms:
                 if mechanism.party.id != vals['party']:
-                    self.raise_user_error('write_party')
-        return super(ContactMechanism, self).write(ids, vals)
-
-ContactMechanism()
+                    cls.raise_user_error('write_party')
+        super(ContactMechanism, cls).write(mechanisms, vals)
diff -r f0d5fbea32c2 trytond/modules/party/party.py
--- a/trytond/modules/party/party.py	Wed Sep 19 22:20:38 2012 +0200
+++ b/trytond/modules/party/party.py	Mon Sep 24 20:36:41 2012 +0200
@@ -7,6 +7,9 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Party', 'PartyCategory', 'CheckVIESNoResult', 'CheckVIESResult',
+           'CheckVIES']
+
 HAS_VATNUMBER = False
 VAT_COUNTRIES = [('', '')]
 try:
@@ -26,8 +29,7 @@
 
 class Party(ModelSQL, ModelView):
     "Party"
-    _description = __doc__
-    _name = "party.party"
+    __name__ = 'party.party'
 
     name = fields.Char('Name', required=True, select=True,
         states=STATES, depends=DEPENDS)
@@ -70,62 +72,64 @@
     email = fields.Function(fields.Char('E-Mail'), 'get_mechanism')
     website = fields.Function(fields.Char('Website'), 'get_mechanism')
 
-    def __init__(self):
-        super(Party, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Party, cls).__setup__()
+        cls._sql_constraints = [
             ('code_uniq', 'UNIQUE(code)',
              'The code of the party must be unique!')
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_vat', 'invalid_vat'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_vat': 'Invalid VAT number!',
         })
-        self._order.insert(0, ('name', 'ASC'))
+        cls._order.insert(0, ('name', 'ASC'))
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_categories(self):
+    @staticmethod
+    def default_categories():
         return Transaction().context.get('categories', [])
 
-    def default_addresses(self):
+    @staticmethod
+    def default_addresses():
         if Transaction().user == 0:
             return []
-        address_obj = Pool().get('party.address')
-        fields_names = list(x for x in set(address_obj._columns.keys()
-                + address_obj._inherit_fields.keys())
+        Address = Pool().get('party.address')
+        fields_names = list(x for x in set(Address._fields.keys()
+                + Address._inherit_fields.keys())
                 if x not in ['id', 'create_uid', 'create_date',
                     'write_uid', 'write_date'])
-        return [address_obj.default_get(fields_names)]
+        return [Address.default_get(fields_names)]
 
-    def default_lang(self):
-        config_obj = Pool().get('party.configuration')
-        config = config_obj.browse(1)
-        return config.party_lang.id
+    @staticmethod
+    def default_lang():
+        Configuration = Pool().get('party.configuration')
+        config = Configuration(1)
+        if config.party_lang:
+            return config.party_lang.id
 
-    def default_code_readonly(self):
-        config_obj = Pool().get('party.configuration')
-        config = config_obj.browse(1)
+    @staticmethod
+    def default_code_readonly():
+        Configuration = Pool().get('party.configuration')
+        config = Configuration(1)
         return bool(config.party_sequence)
 
-    def get_code_readonly(self, ids, name):
-        return dict((x, True) for x in ids)
+    def get_code_readonly(self, name):
+        return True
 
-    def on_change_with_vat_code(self, vals):
-        return (vals.get('vat_country') or '') + (vals.get('vat_number') or '')
+    def on_change_with_vat_code(self):
+        return (self.vat_country or '') + (self.vat_number or '')
 
-    def get_vat_code(self, ids, name):
-        if not ids:
-            return []
-        res = {}
-        for party in self.browse(ids):
-            res[party.id] = ((party.vat_country or '')
-                + (party.vat_number or ''))
-        return res
+    def get_vat_code(self, name):
+        return (self.vat_country or '') + (self.vat_number or '')
 
-    def search_vat_code(self, name, clause):
+    @classmethod
+    def search_vat_code(cls, name, clause):
         res = []
         value = clause[2]
         for country, _ in VAT_COUNTRIES:
@@ -138,150 +142,127 @@
         res.append(('vat_number', clause[1], value))
         return res
 
-    def get_full_name(self, ids, name):
-        if not ids:
-            return []
-        res = {}
-        for party in self.browse(ids):
-            res[party.id] = party.name
-        return res
+    def get_full_name(self, name):
+        return self.name
 
-    def get_mechanism(self, ids, name):
-        if not ids:
-            return []
-        res = {}
-        for party in self.browse(ids):
-            res[party.id] = ''
-            for mechanism in party.contact_mechanisms:
-                if mechanism.type == name:
-                    res[party.id] = mechanism.value
-                    break
-        return res
+    def get_mechanism(self, name):
+        for mechanism in self.contact_mechanisms:
+            if mechanism.type == name:
+                return mechanism.value
+        return ''
 
-    def create(self, values):
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('party.configuration')
+    @classmethod
+    def create(cls, values):
+        Sequence = Pool().get('ir.sequence')
+        Configuration = Pool().get('party.configuration')
 
         values = values.copy()
         if not values.get('code'):
-            config = config_obj.browse(1)
-            values['code'] = sequence_obj.get_id(config.party_sequence.id)
+            config = Configuration(1)
+            values['code'] = Sequence.get_id(config.party_sequence.id)
 
         values['code_length'] = len(values['code'])
-        return super(Party, self).create(values)
+        return super(Party, cls).create(values)
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, parties, vals):
         if vals.get('code'):
             vals = vals.copy()
             vals['code_length'] = len(vals['code'])
-        return super(Party, self).write(ids, vals)
+        super(Party, cls).write(parties, vals)
 
-    def copy(self, ids, default=None):
-        address_obj = Pool().get('party.address')
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+    @classmethod
+    def copy(cls, parties, default=None):
+        Address = Pool().get('party.address')
 
         if default is None:
             default = {}
         default = default.copy()
         default['code'] = None
         default['addresses'] = None
-        new_ids = []
-        for party in self.browse(ids):
-            new_id = super(Party, self).copy(party.id, default=default)
-            address_obj.copy([x.id for x in party.addresses],
+        new_parties = []
+        for party in parties:
+            new_party = super(Party, cls).copy(party, default=default)
+            Address.copy([x.id for x in new_party.addresses],
                     default={
-                        'party': new_id,
+                        'party': new_party.id,
                         })
-            new_ids.append(new_id)
+            new_parties.append(new_party)
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
+        return new_parties
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([('code',) + tuple(clause[1:])], order=[])
-        if ids:
-            ids += self.search([('name',) + tuple(clause[1:])], order=[])
-            return [('id', 'in', ids)]
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        parties = cls.search([('code',) + tuple(clause[1:])], order=[])
+        if parties:
+            parties += cls.search([('name',) + tuple(clause[1:])], order=[])
+
+            return [('id', 'in', [party.id for party in parties])]
         return [('name',) + clause[1:]]
 
-    def address_get(self, party_id, type=None):
+    def address_get(self, type=None):
         """
-        Try to find an address for the given type, if no type match
-        the first address is return.
+        Try to find an address for the given type, if no type matches
+        the first address is returned.
         """
-        address_obj = Pool().get("party.address")
-        address_ids = address_obj.search(
-            [("party", "=", party_id), ("active", "=", True)],
+        Address = Pool().get("party.address")
+        addresses = Address.search(
+            [("party", "=", self.id), ("active", "=", True)],
             order=[('sequence', 'ASC'), ('id', 'ASC')])
-        if not address_ids:
+        if not addresses:
             return None
-        default_address = address_ids[0]
+        default_address = addresses[0]
         if not type:
             return default_address
-        for address in address_obj.browse(address_ids):
-            if address[type]:
-                    return address.id
+        for address in addresses:
+            if getattr(address, type):
+                return address
         return default_address
 
-    def check_vat(self, ids):
+    def check_vat(self):
         '''
         Check the VAT number depending of the country.
         http://sima-pc.com/nif.php
         '''
         if not HAS_VATNUMBER:
             return True
-        for party in self.browse(ids):
-            vat_number = party.vat_number
+        vat_number = self.vat_number
 
-            if not party.vat_country:
-                continue
+        if not self.vat_country:
+            return True
 
-            if not getattr(vatnumber, 'check_vat_' + \
-                    party.vat_country.lower())(vat_number):
+        if not getattr(vatnumber, 'check_vat_' + \
+                self.vat_country.lower())(vat_number):
 
-                #Check if user doesn't have put country code in number
-                if vat_number.startswith(party.vat_country):
-                    vat_number = vat_number[len(party.vat_country):]
-                    self.write(party.id, {
-                        'vat_number': vat_number,
-                        })
-                else:
-                    return False
+            #Check if user doesn't have put country code in number
+            if vat_number.startswith(self.vat_country):
+                vat_number = vat_number[len(self.vat_country):]
+                Party.write([self], {
+                    'vat_number': vat_number,
+                    })
+            else:
+                return False
         return True
 
-Party()
-
 
 class PartyCategory(ModelSQL):
     'Party - Category'
-    _name = 'party.party-party.category'
+    __name__ = 'party.party-party.category'
     _table = 'party_category_rel'
-    _description = __doc__
     party = fields.Many2One('party.party', 'Party', ondelete='CASCADE',
             required=True, select=True)
     category = fields.Many2One('party.category', 'Category',
         ondelete='CASCADE', required=True, select=True)
 
-PartyCategory()
-
 
 class CheckVIESNoResult(ModelView):
     'Check VIES'
-    _name = 'party.check_vies.no_result'
-    _description = __doc__
-
-CheckVIESNoResult()
+    __name__ = 'party.check_vies.no_result'
 
 
 class CheckVIESResult(ModelView):
     'Check VIES'
-    _name = 'party.check_vies.result'
-    _description = __doc__
+    __name__ = 'party.check_vies.result'
     parties_succeed = fields.Many2Many('party.party', None, None,
         'Parties Succeed', readonly=True, states={
             'invisible': ~Eval('parties_succeed'),
@@ -291,12 +272,10 @@
             'invisible': ~Eval('parties_failed'),
             })
 
-CheckVIESResult()
-
 
 class CheckVIES(Wizard):
     'Check VIES'
-    _name = 'party.check_vies'
+    __name__ = 'party.check_vies'
     start_state = 'check'
 
     check = StateTransition()
@@ -309,22 +288,23 @@
             Button('Ok', 'end', 'tryton-ok', True),
             ])
 
-    def __init__(self):
-        super(CheckVIES, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(CheckVIES, cls).__setup__()
+        cls._error_messages.update({
             'vies_unavailable': 'The VIES service is unavailable, ' \
                     'try again later.',
             })
 
-    def transition_check(self, session):
-        party_obj = Pool().get('party.party')
+    def transition_check(self):
+        Party = Pool().get('party.party')
 
         if not HAS_VATNUMBER or not hasattr(vatnumber, 'check_vies'):
             return 'no_result'
 
         parties_succeed = []
         parties_failed = []
-        parties = party_obj.browse(Transaction().context.get('active_ids'))
+        parties = Party.browse(Transaction().context.get('active_ids'))
         for party in parties:
             if not party.vat_code:
                 continue
@@ -345,14 +325,12 @@
                             or e.faultstring.find('SERVER_BUSY'):
                         self.raise_user_error('vies_unavailable')
                 raise
-        session.result.parties_succeed = parties_succeed
-        session.result.parties_failed = parties_failed
+        self.result.parties_succeed = parties_succeed
+        self.result.parties_failed = parties_failed
         return 'result'
 
-    def default_result(self, session, fields):
+    def default_result(self, fields):
         return {
-            'parties_succeed': [p.id for p in session.result.parties_succeed],
-            'parties_failed': [p.id for p in session.result.parties_failed],
+            'parties_succeed': [p.id for p in self.result.parties_succeed],
+            'parties_failed': [p.id for p in self.result.parties_failed],
             }
-
-CheckVIES()
diff -r f0d5fbea32c2 trytond/modules/party/tests/test_party.py
--- a/trytond/modules/party/tests/test_party.py	Wed Sep 19 22:20:38 2012 +0200
+++ b/trytond/modules/party/tests/test_party.py	Mon Sep 24 20:36:41 2012 +0200
@@ -44,10 +44,10 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            category1_id = self.category.create({
+            category1 = self.category.create({
                 'name': 'Category 1',
                 })
-            self.assert_(category1_id)
+            self.assert_(category1.id)
             transaction.cursor.commit()
 
     def test0020category_recursion(self):
@@ -55,20 +55,20 @@
         Test category recursion.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            category1_id = self.category.search([
+            category1, = self.category.search([
                 ('name', '=', 'Category 1'),
-                ], limit=1)[0]
+                ], limit=1)
 
-            category2_id = self.category.create({
+            category2 = self.category.create({
                 'name': 'Category 2',
-                'parent': category1_id,
+                'parent': category1.id,
                 })
-            self.assert_(category2_id)
+            self.assert_(category2.id)
 
-            self.failUnlessRaises(Exception, self.category.write,
-                    category1_id, {
-                        'parent': category2_id,
-                    })
+            self.assertRaises(Exception, self.category.write,
+                [category1], {
+                    'parent': category2.id,
+                })
 
     def test0030party(self):
         '''
@@ -76,10 +76,10 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            party1_id = self.party.create({
+            party1 = self.party.create({
                 'name': 'Party 1',
                 })
-            self.assert_(party1_id)
+            self.assert_(party1.id)
             transaction.cursor.commit()
 
     def test0040party_code(self):
@@ -87,32 +87,32 @@
         Test party code constraint.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            party1_id = self.party.search([], limit=1)[0]
+            party1, = self.party.search([], limit=1)
 
-            code = self.party.read(party1_id, ['code'])['code']
+            code = party1.code
 
-            party2_id = self.party.create({
+            party2 = self.party.create({
                 'name': 'Party 2',
                 })
 
-            self.failUnlessRaises(Exception, self.party.write,
-                    party2_id, {
-                        'code': code,
-                    })
+            self.assertRaises(Exception, self.party.write,
+                [party2], {
+                    'code': code,
+                })
 
     def test0050address(self):
         '''
         Create address.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            party1_id = self.party.search([], limit=1)[0]
+            party1, = self.party.search([], limit=1)
 
-            self.address.create({
-                'party': party1_id,
+            address = self.address.create({
+                'party': party1.id,
                 'street': 'St sample, 15',
                 'city': 'City',
                 })
-            self.assert_(party1_id)
+            self.assert_(address.id)
 
 
 def suite():
diff -r 6ec75b20a2e9 trytond/modules/party_siret/__init__.py
--- a/trytond/modules/party_siret/__init__.py	Fri Jun 08 19:34:05 2012 +0200
+++ b/trytond/modules/party_siret/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,5 +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.
 
+from trytond.pool import Pool
 from .party import *
 from .address import *
+
+
+def register():
+    Pool.register(
+        Party,
+        Address,
+        module='party_siret', type_='model')
diff -r 6ec75b20a2e9 trytond/modules/party_siret/address.py
--- a/trytond/modules/party_siret/address.py	Fri Jun 08 19:34:05 2012 +0200
+++ b/trytond/modules/party_siret/address.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,45 +1,42 @@
 #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 Model, fields
+from trytond.model import fields
 from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 from . import luhn
 
+__all__ = ['Address']
+__metaclass__ = PoolMeta
 
-class Address(Model):
-    _name = 'party.address'
+
+class Address:
+    __name__ = 'party.address'
 
     siret_nic = fields.Char('SIRET NIC', select=True, states={
             'readonly': ~Eval('active', True),
             }, size=5, depends=['active'])
     siret = fields.Function(fields.Char('SIRET'), 'get_siret')
 
-    def __init__(self):
-        super(Address, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Address, cls).__setup__()
+        cls._constraints += [
             ('check_siret', 'invalid_siret'),
-        ]
-        self._error_messages.update({
-            'invalid_siret': 'Invalid SIRET number!',
-        })
+            ]
+        cls._error_messages.update({
+                'invalid_siret': 'Invalid SIRET number!',
+                })
 
-    def get_siret(self, ids, name):
-        res = {}
-        for address in self.browse(ids):
-            if address.party.siren and address.siret_nic:
-                res[address.id] = address.party.siren + address.siret_nic
-            else:
-                res[address.id] = ''
-        return res
+    def get_siret(self, name):
+        if self.party.siren and self.siret_nic:
+            return self.party.siren + self.siret_nic
 
-    def check_siret(self, ids):
+    def check_siret(self):
         '''
         Check validity of SIRET
         '''
-        for address in self.browse(ids):
-            if address.siret:
-                if (len(address.siret) != 14
-                        or not luhn.validate(address.siret)):
-                    return False
+        if self.siret:
+            if (len(self.siret) != 14
+                    or not luhn.validate(self.siret)):
+                return False
         return True
-
-Address()
diff -r 6ec75b20a2e9 trytond/modules/party_siret/party.py
--- a/trytond/modules/party_siret/party.py	Fri Jun 08 19:34:05 2012 +0200
+++ b/trytond/modules/party_siret/party.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,34 +1,36 @@
 #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 Model, fields
+from trytond.model import fields
 from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 from . import luhn
 
+__all__ = ['Party']
+__metaclass__ = PoolMeta
 
-class Party(Model):
-    _name = 'party.party'
+
+class Party:
+    __name__ = 'party.party'
 
     siren = fields.Char('SIREN', select=True, states={
             'readonly': ~Eval('active', True),
             }, size=9, depends=['active'])
 
-    def __init__(self):
-        super(Party, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Party, cls).__setup__()
+        cls._constraints += [
             ('check_siren', 'invalid_siren'),
-        ]
-        self._error_messages.update({
-            'invalid_siren': 'Invalid SIREN number!',
-        })
+            ]
+        cls._error_messages.update({
+                'invalid_siren': 'Invalid SIREN number!',
+                })
 
-    def check_siren(self, ids):
+    def check_siren(self):
         '''
         Check validity of SIREN
         '''
-        for party in self.browse(ids):
-            if party.siren:
-                if len(party.siren) != 9 or not luhn.validate(party.siren):
-                    return False
+        if self.siren:
+            if len(self.siren) != 9 or not luhn.validate(self.siren):
+                return False
         return True
-
-Party()
diff -r 4c693b4704d9 trytond/modules/party_vcarddav/__init__.py
--- a/trytond/modules/party_vcarddav/__init__.py	Tue Jul 31 09:18:55 2012 +0000
+++ b/trytond/modules/party_vcarddav/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,6 +1,19 @@
 #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.pool import Pool
 from . import carddav
 from .webdav import *
 from .party import *
+
+
+def register():
+    Pool.register(
+        Collection,
+        Party,
+        Address,
+        ActionReport,
+        module='party_vcarddav', type_='model')
+    Pool.register(
+        VCard,
+        module='party_vcarddav', type_='report')
diff -r 4c693b4704d9 trytond/modules/party_vcarddav/carddav.py
--- a/trytond/modules/party_vcarddav/carddav.py	Tue Jul 31 09:18:55 2012 +0000
+++ b/trytond/modules/party_vcarddav/carddav.py	Mon Sep 24 20:36:41 2012 +0200
@@ -38,16 +38,15 @@
         raise DAV_NotFound
     pool = Pool(Transaction().cursor.database_name)
     try:
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
     except KeyError:
         raise DAV_NotFound
     try:
-        res = collection_obj.get_address_data(dburi, cache=CACHE)
+        return Collection.get_address_data(dburi, cache=CACHE)
     except DAV_Error:
         raise
     except Exception:
         raise DAV_Error(500)
-    return res
 
 TrytonDAVInterface._get_carddav_address_data = _get_carddav_address_data
 TrytonDAVInterface._get_carddav_addressbook_data = _get_carddav_address_data
diff -r 4c693b4704d9 trytond/modules/party_vcarddav/party.py
--- a/trytond/modules/party_vcarddav/party.py	Tue Jul 31 09:18:55 2012 +0000
+++ b/trytond/modules/party_vcarddav/party.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,98 +1,95 @@
 #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 copy
 import uuid
-from trytond.model import ModelSQL, ModelView, fields
+import vobject
+
+from trytond.model import fields
 from trytond.report import Report
 from trytond.backend import TableHandler, FIELDS
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Party', 'Address', 'ActionReport', 'VCard']
+__metaclass__ = PoolMeta
 
-class Party(ModelSQL, ModelView):
-    _name = 'party.party'
+
+class Party:
+    __name__ = 'party.party'
     uuid = fields.Char('UUID', required=True,
             help='Universally Unique Identifier')
     vcard = fields.Binary('VCard')
 
-    def __init__(self):
-        super(Party, self).__init__()
-        self._sql_constraints += [
-                ('uuid_uniq', 'UNIQUE(uuid)',
-                    'The UUID of the party must be unique!'),
-        ]
+    @classmethod
+    def __setup__(cls):
+        super(Party, cls).__setup__()
+        cls._sql_constraints += [
+            ('uuid_uniq', 'UNIQUE(uuid)',
+                'The UUID of the party must be unique!'),
+            ]
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         if not table.column_exist('uuid'):
             table.add_raw_column('uuid',
-                    FIELDS[self.uuid._type].sql_type(self.uuid),
-                    FIELDS[self.uuid._type].sql_format, None, None)
-            cursor.execute('SELECT id FROM "' + self._table + '"')
+                FIELDS[cls.uuid._type].sql_type(cls.uuid),
+                FIELDS[cls.uuid._type].sql_format, None, None)
+            cursor.execute('SELECT id FROM "' + cls._table + '"')
             for id, in cursor.fetchall():
-                cursor.execute('UPDATE "' + self._table + '" ' \
-                        'SET "uuid" = %s WHERE id = %s',
-                        (self.default_uuid(), id))
-        super(Party, self).init(module_name)
+                cursor.execute('UPDATE "' + cls._table + '" '
+                    'SET "uuid" = %s WHERE id = %s',
+                    (cls.default_uuid(), id))
+        super(Party, cls).__register__(module_name)
 
-    def default_uuid(self):
+    @staticmethod
+    def default_uuid():
         return str(uuid.uuid4())
 
-    def create(self, vals):
-        collection_obj = Pool().get('webdav.collection')
+    @classmethod
+    def create(cls, vals):
+        Collection = Pool().get('webdav.collection')
 
-        res = super(Party, self).create(vals)
+        party = super(Party, cls).create(vals)
         # Restart the cache for vcard
-        collection_obj.vcard.reset()
-        return res
+        Collection._vcard_cache.clear()
+        return party
 
-    def copy(self, ids, default=None):
-        int_id = isinstance(ids, (int, long))
-        if int_id:
-            ids = [ids]
-
+    @classmethod
+    def copy(cls, parties, default=None):
         if default is None:
             default = {}
+        new_parties = []
+        for party in parties:
+            current_default = default.copy()
+            current_default['uuid'] = cls.default_uuid()
+            new_party, = super(Party, cls).copy([party],
+                default=current_default)
+            new_parties.append(new_party)
+        return new_parties
 
-        new_ids = []
-        for party_id in ids:
-            current_default = default.copy()
-            current_default['uuid'] = self.default_uuid()
-            new_id = super(Party, self).copy(party_id, default=current_default)
-            new_ids.append(new_id)
+    @classmethod
+    def write(cls, parties, vals):
+        Collection = Pool().get('webdav.collection')
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
+        super(Party, cls).write(parties, vals)
+        # Restart the cache for vcard
+        Collection._vcard_cache.clear()
 
-    def write(self, ids, vals):
-        collection_obj = Pool().get('webdav.collection')
+    @classmethod
+    def delete(cls, parties):
+        Collection = Pool().get('webdav.collection')
 
-        res = super(Party, self).write(ids, vals)
+        super(Party, cls).delete(parties)
         # Restart the cache for vcard
-        collection_obj.vcard.reset()
-        return res
+        Collection._vcard_cache.clear()
 
-    def delete(self, ids):
-        collection_obj = Pool().get('webdav.collection')
-
-        res = super(Party, self).delete(ids)
-        # Restart the cache for vcard
-        collection_obj.vcard.reset()
-        return res
-
-    def vcard2values(self, party_id, vcard):
+    def vcard2values(self, vcard):
         '''
         Convert vcard to values for create or write
-
-        :param party_id: the party id for write or None for create
-        :param vcard: a vcard instance of vobject
-        :return: a dictionary with values
         '''
-        import vobject
-        address_obj = Pool().get('party.address')
+        Address = Pool().get('party.address')
 
         res = {}
         res['name'] = vcard.fn.value
@@ -100,12 +97,12 @@
             vcard.add('n')
             vcard.n.value = vobject.vcard.Name(vcard.fn.value)
         res['vcard'] = vcard.serialize()
-        if not party_id:
+        if not self.id:
             if hasattr(vcard, 'uid'):
                 res['uuid'] = vcard.uid.value
             res['addresses'] = []
             for adr in vcard.contents.get('adr', []):
-                vals = address_obj.vcard2values(adr)
+                vals = Address.vcard2values(adr)
                 res['addresses'].append(('create', vals))
             res['contact_mechanisms'] = []
             for email in vcard.contents.get('email', []):
@@ -122,11 +119,10 @@
                 vals['value'] = tel.value
                 res['contact_mechanisms'].append(('create', vals))
         else:
-            party = self.browse(party_id)
             i = 0
             res['addresses'] = []
             addresses_todelete = []
-            for address in party.addresses:
+            for address in self.addresses:
                 try:
                     adr = vcard.contents.get('adr', [])[i]
                 except IndexError:
@@ -137,8 +133,8 @@
                     addresses_todelete.append(address.id)
                     i += 1
                     continue
-                vals = address_obj.vcard2values(adr)
-                res['addresses'].append(('write', address.id, vals))
+                vals = Address.vcard2values(adr)
+                res['addresses'].append(('write', [address.id], vals))
                 i += 1
             if addresses_todelete:
                 res['addresses'].append(('delete', addresses_todelete))
@@ -149,13 +145,13 @@
             for adr in new_addresses:
                 if not hasattr(adr, 'value'):
                     continue
-                vals = address_obj.vcard2values(adr)
+                vals = Address.vcard2values(adr)
                 res['addresses'].append(('create', vals))
 
             i = 0
             res['contact_mechanisms'] = []
             contact_mechanisms_todelete = []
-            for cm in party.contact_mechanisms:
+            for cm in self.contact_mechanisms:
                 if cm.type != 'email':
                     continue
                 try:
@@ -181,7 +177,7 @@
                 res['contact_mechanisms'].append(('create', vals))
 
             i = 0
-            for cm in party.contact_mechanisms:
+            for cm in self.contact_mechanisms:
                 if cm.type not in ('phone', 'mobile'):
                     continue
                 try:
@@ -214,89 +210,79 @@
                     contact_mechanisms_todelete))
         return res
 
-Party()
 
-
-class Address(ModelSQL, ModelView):
-    _name = 'party.address'
+class Address:
+    __name__ = 'party.address'
 
     def vcard2values(self, adr):
         '''
         Convert adr from vcard to values for create or write
-
-        :param adr: a adr from vcard instance of vobject
-        :return: a dictionary with values
         '''
-        country_obj = Pool().get('country.country')
-        subdivision_obj = Pool().get('country.subdivision')
+        pool = Pool()
+        Country = pool.get('country.country')
+        Subdivision = pool.get('country.subdivision')
 
         vals = {}
         vals['street'] = adr.value.street or ''
         vals['city'] = adr.value.city or ''
         vals['zip'] = adr.value.code or ''
         if adr.value.country:
-            country_ids = country_obj.search([
-                ('rec_name', '=', adr.value.country),
-                ], limit=1)
-            if country_ids:
-                vals['country'] = country_ids[0]
+            countries = Country.search([
+                    ('rec_name', '=', adr.value.country),
+                    ], limit=1)
+            if countries:
+                country, = countries
+                vals['country'] = country.id
                 if adr.value.region:
-                    subdivision_ids = subdivision_obj.search([
+                    subdivisions = Subdivision.search([
                             ('rec_name', '=', adr.value.region),
-                            ('country', '=', country_ids[0]),
+                            ('country', '=', country.id),
                             ], limit=1)
-                    if subdivision_ids:
-                        vals['subdivision'] = subdivision_ids[0]
+                    if subdivisions:
+                        subdivision, = subdivisions
+                        vals['subdivision'] = subdivision.id
         return vals
 
-Address()
 
+class ActionReport:
+    __name__ = 'ir.action.report'
 
-class ActionReport(ModelSQL, ModelView):
-    _name = 'ir.action.report'
-
-    def __init__(self):
-        super(ActionReport, self).__init__()
+    @classmethod
+    def __setup__(cls):
+        super(ActionReport, cls).__setup__()
         new_ext = ('vcf', 'VCard file')
-        if new_ext not in self.extension.selection:
-            self.extension = copy.copy(self.extension)
-            self.extension.selection = copy.copy(self.extension.selection)
-            self.extension.selection.append(new_ext)
-            self._reset_columns()
-
-ActionReport()
+        if new_ext not in cls.extension.selection:
+            cls.extension.selection.append(new_ext)
 
 
 class VCard(Report):
-    _name = 'party_vcarddav.party.vcard'
+    __name__ = 'party_vcarddav.party.vcard'
 
-    def execute(self, ids, datas):
-        party_obj = Pool().get('party.party')
-        action_report_obj = Pool().get('ir.action.report')
+    @classmethod
+    def execute(cls, ids, data):
+        pool = Pool()
+        Party = pool.get('party.party')
+        ActionReport = pool.get('ir.action.report')
 
-        action_report_ids = action_report_obj.search([
-            ('report_name', '=', self._name)
-            ])
-        if not action_report_ids:
-            raise Exception('Error', 'Report (%s) not find!' % self._name)
-        action_report = action_report_obj.browse(action_report_ids[0])
+        action_reports = ActionReport.search([
+                ('report_name', '=', cls.__name__)
+                ])
+        if not action_reports:
+            raise Exception('Error', 'Report (%s) not find!' % cls.__name__)
+        action_report = action_reports[0]
 
-        parties = party_obj.browse(ids)
+        parties = Party(ids)
 
-        data = ''.join(self.create_vcard(party).serialize()
+        data = ''.join(cls.create_vcard(party).serialize()
             for party in parties)
 
         return ('vcf', buffer(data), False, action_report.name)
 
-    def create_vcard(self, party):
+    @classmethod
+    def create_vcard(cls, party):
         '''
         Return a vcard instance of vobject for the party
-
-        :param party: a BrowseRecord of party.party
-        :return: a vcard instance of vobject
         '''
-        import vobject
-
         if party.vcard:
             vcard = vobject.readOne(str(party.vcard))
         else:
@@ -387,5 +373,3 @@
             vcard.contents['tel'].remove(tel)
 
         return vcard
-
-VCard()
diff -r 4c693b4704d9 trytond/modules/party_vcarddav/webdav.py
--- a/trytond/modules/party_vcarddav/webdav.py	Tue Jul 31 09:18:55 2012 +0000
+++ b/trytond/modules/party_vcarddav/webdav.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,53 +1,56 @@
 #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 pywebdav.lib.errors import DAV_NotFound, DAV_Forbidden
-from trytond.model import ModelView, ModelSQL
 from trytond.tools import reduce_ids
 from trytond.transaction import Transaction
 from trytond.cache import Cache
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
+
+__all__ = ['Collection']
+__metaclass__ = PoolMeta
 
 
 CARDDAV_NS = 'urn:ietf:params:xml:ns:carddav'
 
 
-class Collection(ModelSQL, ModelView):
+class Collection:
+    __name__ = "webdav.collection"
+    _vcard_cache = Cache('webdav.collection.vcard', context=False)
 
-    _name = "webdav.collection"
-
-    @Cache('webdav_collection.vcard')
-    def vcard(self, uri):
+    @classmethod
+    def vcard(cls, uri):
         '''
         Return party ids of the vcard in uri or False
-
-        :param uri: the uri
-        :return: party id
-            or None if there is no party
-            or False if not in Contacts
         '''
-        party_obj = Pool().get('party.party')
+        Party = Pool().get('party.party')
+        party_id = cls._vcard_cache.get(uri, -1)
+        if party_id != -1:
+            return party_id
 
         if uri and uri.startswith('Contacts/'):
             uuid = uri[9:-4]
-            party_ids = party_obj.search([
-                ('uuid', '=', uuid),
-                ], limit=1)
-            if party_ids:
-                return party_ids[0]
-            return None
-        if uri == 'Contacts':
-            return None
-        return False
+            parties = Party.search([
+                    ('uuid', '=', uuid),
+                    ], limit=1)
+            if parties:
+                party_id = parties[0].id
+            else:
+                party_id = None
+        elif uri == 'Contacts':
+            party_id = None
+        else:
+            party_id = False
+        cls._vcard_cache.set(uri, party_id)
+        return party_id
 
-    def _carddav_filter_domain(self, filter):
+    @classmethod
+    def _carddav_filter_domain(cls, filter):
         '''
         Return a domain for the carddav filter
-
-        :param filter: the DOM Element of filter
-        :return: a list for domain
         '''
-        address_obj = Pool().get('party.address')
-        contact_mechanism_obj = Pool().get('party.contact_mechanism')
+        pool = Pool()
+        Address = pool.get('party.address')
+        ContactMechanism = pool.get('party.contact_mechanism')
 
         res = []
         if not filter:
@@ -110,60 +113,64 @@
                             res2.append((field, 'not ilike', value))
                     if name == 'adr':
                         domain = res2
-                        address_ids = address_obj.search(domain)
-                        res = [('addresses', 'in', address_ids)]
+                        addresses = Address.search(domain)
+                        res = [('addresses', 'in', [a.id for a in addresses])]
                     elif name in ('mail', 'tel'):
                         if name == 'mail':
                             type = ['email']
                         else:
                             type = ['phone', 'mobile']
                         domain = [('type', 'in', type), res2]
-                        contact_mechanism_ids = contact_mechanism_obj.search(
-                                domain)
+                        contact_mechanisms = ContactMechanism.search(
+                            domain)
                         res2 = [
-                            ('contact_mechanisms', 'in', contact_mechanism_ids)
+                            ('contact_mechanisms', 'in',
+                                [c.id for c in contact_mechanisms])
                             ]
                     res.append(res2)
         return res
 
-    def get_childs(self, uri, filter=None, cache=None):
-        party_obj = Pool().get('party.party')
+    @classmethod
+    def get_childs(cls, uri, filter=None, cache=None):
+        Party = Pool().get('party.party')
 
         if uri in ('Contacts', 'Contacts/'):
-            domain = self._carddav_filter_domain(filter)
-            party_ids = party_obj.search(domain)
-            parties = party_obj.browse(party_ids)
+            domain = cls._carddav_filter_domain(filter)
+            parties = Party.search(domain)
             if cache is not None:
                 cache.setdefault('_contact', {})
-                for party_id in party_ids:
-                    cache['_contact'][party_id] = {}
+                for party in parties:
+                    cache['_contact'][party.id] = {}
             return [x.uuid + '.vcf' for x in parties]
-        party_id = self.vcard(uri)
+        party_id = cls.vcard(uri)
         if party_id or party_id is None:
             return []
-        res = super(Collection, self).get_childs(uri, filter=filter,
+        res = super(Collection, cls).get_childs(uri, filter=filter,
                 cache=cache)
         if not uri and not filter:
             res.append('Contacts')
         return res
 
-    def get_resourcetype(self, uri, cache=None):
+    @classmethod
+    def get_resourcetype(cls, uri, cache=None):
         from DAV.constants import COLLECTION, OBJECT
-        party_id = self.vcard(uri)
+        party_id = cls.vcard(uri)
         if party_id:
             return OBJECT
         elif party_id is None:
             return COLLECTION
-        return super(Collection, self).get_resourcetype(uri, cache=cache)
+        return super(Collection, cls).get_resourcetype(uri, cache=cache)
 
-    def get_contenttype(self, uri, cache=None):
-        if self.vcard(uri):
+    @classmethod
+    def get_contenttype(cls, uri, cache=None):
+        if cls.vcard(uri):
             return 'text/x-vcard'
-        return super(Collection, self).get_contenttype(uri, cache=cache)
+        return super(Collection, cls).get_contenttype(uri, cache=cache)
 
-    def get_creationdate(self, uri, cache=None):
-        party_obj = Pool().get('party.party')
-        party_id = self.vcard(uri)
+    @classmethod
+    def get_creationdate(cls, uri, cache=None):
+        Party = Pool().get('party.party')
+        party_id = cls.vcard(uri)
 
         cursor = Transaction().cursor
 
@@ -185,7 +192,7 @@
                 red_sql, red_ids = reduce_ids('id', sub_ids)
                 cursor.execute('SELECT id, ' \
                             'EXTRACT(epoch FROM create_date) ' \
-                        'FROM "' + party_obj._table + '" ' \
+                        'FROM "' + Party._table + '" ' \
                         'WHERE ' + red_sql, red_ids)
                 for party_id2, date in cursor.fetchall():
                     if party_id2 == party_id:
@@ -195,17 +202,18 @@
                         cache['_contact'][party_id2]['creationdate'] = date
             if res is not None:
                 return res
-        return super(Collection, self).get_creationdate(uri, cache=cache)
+        return super(Collection, cls).get_creationdate(uri, cache=cache)
 
-    def get_lastmodified(self, uri, cache=None):
+    @classmethod
+    def get_lastmodified(cls, uri, cache=None):
         pool = Pool()
-        party_obj = pool.get('party.party')
-        address_obj = pool.get('party.address')
-        contact_mechanism_obj = pool.get('party.contact_mechanism')
+        Party = pool.get('party.party')
+        Address = pool.get('party.address')
+        ContactMechanism = pool.get('party.contact_mechanism')
 
         cursor = Transaction().cursor
 
-        party_id = self.vcard(uri)
+        party_id = cls.vcard(uri)
         if party_id:
             if cache is not None:
                 cache.setdefault('_contact', {})
@@ -227,10 +235,10 @@
                             'COALESCE(a.write_date, a.create_date))), '
                         'MAX(EXTRACT(epoch FROM '
                             'COALESCE(c.write_date, c.create_date))) '
-                    'FROM "' + party_obj._table + '" p '
-                    'LEFT JOIN "' + address_obj._table + '" a '
+                    'FROM "' + Party._table + '" p '
+                    'LEFT JOIN "' + Address._table + '" a '
                         'ON p.id = a.party '
-                    'LEFT JOIN "' + contact_mechanism_obj._table + '" c '
+                    'LEFT JOIN "' + ContactMechanism._table + '" c '
                         'ON p.id = c.party '
                     'WHERE ' + red_sql + ' '
                     'GROUP BY p.id', red_ids)
@@ -243,93 +251,98 @@
                         cache['_contact'][party_id2]['lastmodified'] = date
             if res is not None:
                 return res
-        return super(Collection, self).get_lastmodified(uri, cache=cache)
+        return super(Collection, cls).get_lastmodified(uri, cache=cache)
 
-    def get_data(self, uri, cache=None):
-        vcard_obj = Pool().get('party_vcarddav.party.vcard', type='report')
-        party_id = self.vcard(uri)
+    @classmethod
+    def get_data(cls, uri, cache=None):
+        Vcard = Pool().get('party_vcarddav.party.vcard', type='report')
+        party_id = cls.vcard(uri)
         if party_id is None:
             raise DAV_NotFound
         if party_id:
-            val = vcard_obj.execute([party_id],
-                    {'id': party_id, 'ids': [party_id]},
-                    )
+            val = Vcard.execute([party_id],
+                {'id': party_id, 'ids': [party_id]})
             return val[1]
-        return super(Collection, self).get_data(uri, cache=cache)
+        return super(Collection, cls).get_data(uri, cache=cache)
 
-    def get_address_data(self, uri, cache=None):
-        vcard_obj = Pool().get('party_vcarddav.party.vcard', type='report')
-        party_id = self.vcard(uri)
+    @classmethod
+    def get_address_data(cls, uri, cache=None):
+        Vcard = Pool().get('party_vcarddav.party.vcard', type='report')
+        party_id = cls.vcard(uri)
         if not party_id:
             raise DAV_NotFound
-        return vcard_obj.execute([party_id],
+        return Vcard.execute([party_id],
             {'id': party_id, 'ids': [party_id]},
             ).decode('utf-8')
 
-    def put(self, uri, data, content_type, cache=None):
+    @classmethod
+    def put(cls, uri, data, content_type, cache=None):
         import vobject
-        party_obj = Pool().get('party.party')
+        Party = Pool().get('party.party')
 
-        party_id = self.vcard(uri)
+        party_id = cls.vcard(uri)
         if party_id is None:
             vcard = vobject.readOne(data)
-            values = party_obj.vcard2values(None, vcard)
+            values = Party().vcard2values(vcard)
             try:
-                party_id = party_obj.create(values)
+                party_id = Party.create(values)
             except Exception:
                 raise DAV_Forbidden
-            party = party_obj.browse(party_id)
+            party = Party(party_id)
             return (Transaction().cursor.database_name + '/Contacts/' +
                     party.uuid + '.vcf')
         if party_id:
+            party = Party(party_id)
             vcard = vobject.readOne(data)
-            values = party_obj.vcard2values(party_id, vcard)
+            values = party.vcard2values(vcard)
             try:
-                party_obj.write(party_id, values)
+                Party.write([party], values)
             except Exception:
                 raise DAV_Forbidden
             return
-        return super(Collection, self).put(uri, data, content_type,
+        return super(Collection, cls).put(uri, data, content_type,
             cache=cache)
 
-    def mkcol(self, uri, cache=None):
-        party_id = self.vcard(uri)
+    @classmethod
+    def mkcol(cls, uri, cache=None):
+        party_id = cls.vcard(uri)
         if party_id is None:
             raise DAV_Forbidden
         if party_id:
             raise DAV_Forbidden
-        return super(Collection, self).mkcol(uri, cache=cache)
+        return super(Collection, cls).mkcol(uri, cache=cache)
 
-    def rmcol(self, uri, cache=None):
-        party_id = self.vcard(uri)
+    @classmethod
+    def rmcol(cls, uri, cache=None):
+        party_id = cls.vcard(uri)
         if party_id is None:
             raise DAV_Forbidden
         if party_id:
             raise DAV_Forbidden
-        return super(Collection, self).rmcol(uri, cache=cache)
+        return super(Collection, cls).rmcol(uri, cache=cache)
 
-    def rm(self, uri, cache=None):
-        party_obj = Pool().get('party.party')
+    @classmethod
+    def rm(cls, uri, cache=None):
+        Party = Pool().get('party.party')
 
-        party_id = self.vcard(uri)
+        party_id = cls.vcard(uri)
         if party_id is None:
             raise DAV_Forbidden
         if party_id:
             try:
-                party_obj.delete(party_id)
+                Party.delete([Party(party_id)])
             except Exception:
                 raise DAV_Forbidden
             return 200
-        return super(Collection, self).rm(uri, cache=cache)
+        return super(Collection, cls).rm(uri, cache=cache)
 
-    def exists(self, uri, cache=None):
-        party_id = self.vcard(uri)
+    @classmethod
+    def exists(cls, uri, cache=None):
+        party_id = cls.vcard(uri)
         if party_id is None or party_id:
             if party_id:
                 return 1
             if uri in ('Contacts', 'Contacts/'):
                 return 1
             return 0
-        return super(Collection, self).exists(uri, cache=cache)
-
-Collection()
+        return super(Collection, cls).exists(uri, cache=cache)
diff -r 73af8be8415d trytond/modules/product/__init__.py
--- a/trytond/modules/product/__init__.py	Thu Aug 23 23:18:16 2012 +0200
+++ b/trytond/modules/product/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,6 +1,17 @@
 #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.pool import Pool
 from .uom import *
 from .category import *
 from .product import *
+
+
+def register():
+    Pool.register(
+        UomCategory,
+        Uom,
+        Category,
+        Template,
+        Product,
+        module='product', type_='model')
diff -r 73af8be8415d trytond/modules/product/category.py
--- a/trytond/modules/product/category.py	Thu Aug 23 23:18:16 2012 +0200
+++ b/trytond/modules/product/category.py	Mon Sep 24 20:36:41 2012 +0200
@@ -2,42 +2,32 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelView, ModelSQL, fields
 
+__all__ = ['Category']
+
 
 class Category(ModelSQL, ModelView):
     "Product Category"
-    _name = "product.category"
-    _description = __doc__
-
+    __name__ = "product.category"
     name = fields.Char('Name', required=True, translate=True)
     parent = fields.Many2One('product.category', 'Parent', select=True)
     childs = fields.One2Many('product.category', 'parent',
             string='Children')
 
-    def __init__(self):
-        super(Category, self).__init__()
-        self._order.insert(0, ('name', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(Category, cls).__setup__()
+        cls._order.insert(0, ('name', 'ASC'))
 
-        self._constraints += [
+        cls._constraints += [
             ('check_recursion', 'recursive_categories'),
-        ]
-        self._error_messages.update({
-            'recursive_categories': 'You can not create recursive categories!',
-        })
+            ]
+        cls._error_messages.update({
+                'recursive_categories': \
+                    'You can not create recursive categories!',
+                })
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-
-        def _name(category):
-            if category.id in res:
-                return res[category.id]
-            elif category.parent:
-                return _name(category.parent) + ' / ' + category.name
-            else:
-                return category.name
-        for category in self.browse(ids):
-            res[category.id] = _name(category)
-        return res
-
-Category()
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.get_rec_name(name) + ' / ' + self.name
+        else:
+            return self.name
diff -r 73af8be8415d trytond/modules/product/product.py
--- a/trytond/modules/product/product.py	Thu Aug 23 23:18:16 2012 +0200
+++ b/trytond/modules/product/product.py	Mon Sep 24 20:36:41 2012 +0200
@@ -6,6 +6,8 @@
 from trytond.pool import Pool
 from trytond.backend import TableHandler
 
+__all__ = ['Template', 'Product']
+
 STATES = {
     'readonly': ~Eval('active', True),
     }
@@ -14,9 +16,7 @@
 
 class Template(ModelSQL, ModelView):
     "Product Template"
-    _name = "product.template"
-    _description = __doc__
-
+    __name__ = "product.template"
     name = fields.Char('Name', size=None, required=True, translate=True,
         select=True, states=STATES, depends=DEPENDS)
     type = fields.Selection([
@@ -50,146 +50,122 @@
     default_uom_category = fields.Function(
         fields.Many2One('product.uom.category', 'Default UOM Category',
             on_change_with=['default_uom']),
-        'get_default_uom_category')
+        'on_change_with_default_uom_category')
     active = fields.Boolean('Active', select=True)
     products = fields.One2Many('product.product', 'template', 'Products',
         states=STATES, depends=DEPENDS)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
 
-        super(Template, self).init(module_name)
+        super(Template, cls).__register__(module_name)
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         # Migration from 2.2: category is no more required
         table.not_null_action('category', 'remove')
 
         # Migration from 2.2: new types
-        cursor.execute('UPDATE "' + self._table + '" '
+        cursor.execute('UPDATE "' + cls._table + '" '
             'SET consumable = %s WHERE type = %s', (True, 'consumable'))
-        cursor.execute('UPDATE "' + self._table + '" '
+        cursor.execute('UPDATE "' + cls._table + '" '
             'SET type = %s WHERE type IN (%s, %s)',
             ('goods', 'stockable', 'consumable'))
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'goods'
 
-    def default_consumable(self):
+    @staticmethod
+    def default_consumable():
         return False
 
-    def default_cost_price_method(self):
+    @staticmethod
+    def default_cost_price_method():
         return 'fixed'
 
-    def on_change_with_default_uom_category(self, values):
-        pool = Pool()
-        uom_obj = pool.get('product.uom')
-        if values.get('default_uom'):
-            uom = uom_obj.browse(values['default_uom'])
-            return uom.category.id
+    def on_change_with_default_uom_category(self, name=None):
+        if self.default_uom:
+            return self.default_uom.category.id
 
-    def get_default_uom_category(self, ids, name):
-        categories = {}
-        for template in self.browse(ids):
-            categories[template.id] = template.default_uom.category.id
-        return categories
-
-    def get_price_uom(self, ids, name):
-        product_uom_obj = Pool().get('product.uom')
+    @staticmethod
+    def get_price_uom(products, name):
+        Uom = Pool().get('product.uom')
         res = {}
         field = name[:-4]
         if Transaction().context.get('uom'):
-            to_uom = product_uom_obj.browse(
-                Transaction().context['uom'])
-            for product in self.browse(ids):
-                res[product.id] = product_uom_obj.compute_price(
-                        product.default_uom, product[field], to_uom)
+            to_uom = Uom(Transaction().context['uom'])
+            for product in products:
+                res[product.id] = Uom.compute_price(
+                    product.default_uom, getattr(product, field), to_uom)
         else:
-            for product in self.browse(ids):
-                res[product.id] = product[field]
+            for product in products:
+                res[product.id] = getattr(product, field)
         return res
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, templates, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default['products'] = None
-        return super(Template, self).copy(ids, default=default)
-
-Template()
+        return super(Template, cls).copy(templates, default=default)
 
 
 class Product(ModelSQL, ModelView):
     "Product"
-    _name = "product.product"
-    _description = __doc__
+    __name__ = "product.product"
     _inherits = {'product.template': 'template'}
-
     template = fields.Many2One('product.template', 'Product Template',
             required=True, ondelete='CASCADE', select=True)
     code = fields.Char("Code", size=None, select=True)
     description = fields.Text("Description", translate=True)
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for product in self.browse(ids):
-            name = product.name
-            if product.code:
-                name = '[' + product.code + '] ' + product.name
-            res[product.id] = name
-        return res
+    def get_rec_name(self, name):
+        if self.code:
+            return '[' + self.code + '] ' + self.name
+        else:
+            return self.name
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([('code',) + clause[1:]], order=[])
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        ids = map(int, cls.search([('code',) + clause[1:]], order=[]))
         if ids:
-            ids += self.search([('name',) + clause[1:]], order=[])
+            ids += map(int, cls.search([('name',) + clause[1:]], order=[]))
             return [('id', 'in', ids)]
         return [('name',) + clause[1:]]
 
-    def delete(self, ids):
-        template_obj = Pool().get('product.template')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, products):
+        Template = Pool().get('product.template')
 
         # Get the templates before we delete the products.
-        products = self.browse(ids)
-        template_ids = [product.template.id for product in products]
+        templates = [product.template for product in products]
 
-        res = super(Product, self).delete(ids)
+        super(Product, cls).delete(products)
 
         # Get templates that are still linked after delete.
-        templates = template_obj.browse(template_ids)
-        unlinked_template_ids = [template.id for template in templates \
-                                 if not template.products]
-        if unlinked_template_ids:
-            template_obj.delete(unlinked_template_ids)
+        unlinked_templates = [template for template in templates
+            if not template.products]
+        if unlinked_templates:
+            Template.delete(unlinked_templates)
 
-        return res
+    @classmethod
+    def copy(cls, products, default=None):
+        Template = Pool().get('product.template')
 
-    def copy(self, ids, default=None):
-        template_obj = Pool().get('product.template')
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
         default['products'] = None
-        new_ids = []
-        for product in self.browse(ids):
-            default['template'] = template_obj.copy(product.template.id)
-            new_id = super(Product, self).copy(product.id, default=default)
-            new_ids.append(new_id)
-
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-Product()
+        new_products = []
+        for product in products:
+            template, = Template.copy([product.template])
+            default['template'] = template.id
+            new_products.extend(super(Product, cls).copy([product],
+                    default=default))
+        return new_products
diff -r 73af8be8415d trytond/modules/product/tests/test_product.py
--- a/trytond/modules/product/tests/test_product.py	Thu Aug 23 23:18:16 2012 +0200
+++ b/trytond/modules/product/tests/test_product.py	Mon Sep 24 20:36:41 2012 +0200
@@ -45,41 +45,41 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            category_id = self.uom_category.create({'name': 'Test'})
+            category = self.uom_category.create({'name': 'Test'})
             transaction.cursor.commit()
 
-            self.failUnlessRaises(Exception, self.uom.create, {
-                'name': 'Test',
-                'symbol': 'T',
-                'category': category_id,
-                'rate': 0,
-                'factor': 0,
-                })
+            self.assertRaises(Exception, self.uom.create, {
+                    'name': 'Test',
+                    'symbol': 'T',
+                    'category': category.id,
+                    'rate': 0,
+                    'factor': 0,
+                    })
             transaction.cursor.rollback()
 
-            uom_id = self.uom.create({
-                'name': 'Test',
-                'symbol': 'T',
-                'category': category_id,
-                'rate': 1.0,
-                'factor': 1.0,
-                })
+            uom = self.uom.create({
+                    'name': 'Test',
+                    'symbol': 'T',
+                    'category': category.id,
+                    'rate': 1.0,
+                    'factor': 1.0,
+                    })
             transaction.cursor.commit()
 
-            self.failUnlessRaises(Exception, self.uom.write, uom_id, {
-                'rate': 0.0,
-                })
+            self.assertRaises(Exception, self.uom.write, [uom], {
+                    'rate': 0.0,
+                    })
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.uom.write, uom_id, {
-                'factor': 0.0,
-                })
+            self.assertRaises(Exception, self.uom.write, [uom], {
+                    'factor': 0.0,
+                    })
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.uom.write, uom_id, {
-                'rate': 0.0,
-                'factor': 0.0,
-                })
+            self.assertRaises(Exception, self.uom.write, [uom], {
+                    'rate': 0.0,
+                    'factor': 0.0,
+                    })
             transaction.cursor.rollback()
 
     def test0020uom_check_factor_and_rate(self):
@@ -88,33 +88,33 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            category_id = self.uom_category.search([
-                ('name', '=', 'Test'),
-                ], limit=1)[0]
+            category, = self.uom_category.search([
+                    ('name', '=', 'Test'),
+                    ], limit=1)
 
-            self.failUnlessRaises(Exception, self.uom.create, {
-                'name': 'Test',
-                'symbol': 'T',
-                'category': category_id,
-                'rate': 2,
-                'factor': 2,
-                })
+            self.assertRaises(Exception, self.uom.create, {
+                    'name': 'Test',
+                    'symbol': 'T',
+                    'category': category.id,
+                    'rate': 2,
+                    'factor': 2,
+                    })
             transaction.cursor.rollback()
 
-            uom_id = self.uom.search([
-                ('name', '=', 'Test'),
-                ], limit=1)[0]
+            uom, = self.uom.search([
+                    ('name', '=', 'Test'),
+                    ], limit=1)
 
-            self.failUnlessRaises(Exception, self.uom.write, uom_id,
-                    {
-                        'rate': 2.0,
-                        })
+            self.assertRaises(Exception, self.uom.write, [uom],
+                {
+                    'rate': 2.0,
+                    })
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.uom.write, uom_id,
-                    {
-                        'factor': 2.0,
-                        })
+            self.assertRaises(Exception, self.uom.write, [uom],
+                {
+                    'factor': 2.0,
+                    })
             transaction.cursor.rollback()
 
     def test0030uom_select_accurate_field(self):
@@ -126,14 +126,13 @@
             ('Kilometer', 'factor'),
             ('centimeter', 'rate'),
             ('Foot', 'factor'),
-        ]
+            ]
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
             for name, result in tests:
-                uom_id = self.uom.search([
-                    ('name', '=', name),
-                    ], limit=1)[0]
-                uom = self.uom.browse(uom_id)
-                self.assert_(result == self.uom.select_accurate_field(uom))
+                uom, = self.uom.search([
+                        ('name', '=', name),
+                        ], limit=1)
+                self.assertEqual(result, uom.accurate_field)
 
     def test0040uom_compute_qty(self):
         '''
@@ -145,28 +144,28 @@
             ('Second', 5, 'Minute', 0.083333333333333343, 0.08),
             ('Second', 25, 'Hour', 0.0069444444444444441, 0.01),
             ('Millimeter', 3, 'Inch', 0.11811023622047245, 0.12),
-        ]
+            ]
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
             for from_name, qty, to_name, result, rounded_result in tests:
-                from_uom = self.uom.browse(self.uom.search(
-                    [
-                     ('name', '=', from_name),
-                    ], limit=1)[0])
-                to_uom = self.uom.browse(self.uom.search(
-                    [
+                from_uom, = self.uom.search([
+                        ('name', '=', from_name),
+                        ], limit=1)
+                to_uom, = self.uom.search([
                         ('name', '=', to_name),
-                    ], limit=1)[0])
-                self.assert_(result == self.uom.compute_qty(from_uom,
-                    qty, to_uom, False))
-                self.assert_(rounded_result == self.uom.compute_qty(
-                    from_uom, qty, to_uom, True))
+                        ], limit=1)
+                self.assertEqual(result, self.uom.compute_qty(from_uom,
+                        qty, to_uom, False))
+                self.assertEqual(rounded_result, self.uom.compute_qty(
+                        from_uom, qty, to_uom, True))
 
-            self.assert_(10 == self.uom.compute_qty(None, 10, to_uom))
-            self.assert_(10 == self.uom.compute_qty(None, 10, to_uom, True))
-            self.assert_(0 == self.uom.compute_qty(from_uom, 0, to_uom))
-            self.assert_(0 == self.uom.compute_qty(from_uom, 0, to_uom, True))
-            self.assert_(10 == self.uom.compute_qty(from_uom, 10, None))
-            self.assert_(10 == self.uom.compute_qty(from_uom, 10, None, True))
+            self.assertEqual(10, self.uom.compute_qty(None, 10, to_uom))
+            self.assertEqual(10, self.uom.compute_qty(None, 10, to_uom, True))
+            self.assertEqual(0, self.uom.compute_qty(from_uom, 0, to_uom))
+            self.assertEqual(0,
+                self.uom.compute_qty(from_uom, 0, to_uom, True))
+            self.assertEqual(10, self.uom.compute_qty(from_uom, 10, None))
+            self.assertEqual(10,
+                self.uom.compute_qty(from_uom, 10, None, True))
 
     def test0050uom_compute_price(self):
         '''
@@ -178,19 +177,17 @@
             ('Second', Decimal('5'), 'Minute', Decimal('300')),
             ('Second', Decimal('25'), 'Hour', Decimal('90000')),
             ('Millimeter', Decimal('3'), 'Inch', Decimal('76.2')),
-        ]
+            ]
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
             for from_name, price, to_name, result in tests:
-                from_uom = self.uom.browse(self.uom.search(
-                    [
+                from_uom, = self.uom.search([
                         ('name', '=', from_name),
-                    ], limit=1)[0])
-                to_uom = self.uom.browse(self.uom.search(
-                    [
+                        ], limit=1)
+                to_uom, = self.uom.search([
                         ('name', '=', to_name),
-                    ], limit=1)[0])
-                self.assert_(result == self.uom.compute_price(from_uom,
-                    price, to_uom))
+                        ], limit=1)
+                self.assertEqual(result, self.uom.compute_price(from_uom,
+                        price, to_uom))
 
 
 def suite():
diff -r 73af8be8415d trytond/modules/product/uom.py
--- a/trytond/modules/product/uom.py	Thu Aug 23 23:18:16 2012 +0200
+++ b/trytond/modules/product/uom.py	Mon Sep 24 20:36:41 2012 +0200
@@ -5,6 +5,8 @@
 from trytond.pyson import Eval
 from trytond.transaction import Transaction
 
+__all__ = ['UomCategory', 'Uom']
+
 STATES = {
     'readonly': ~Eval('active', True),
     }
@@ -13,22 +15,19 @@
 
 class UomCategory(ModelSQL, ModelView):
     'Product uom category'
-    _name = 'product.uom.category'
-    _description = __doc__
+    __name__ = 'product.uom.category'
     name = fields.Char('Name', required=True, translate=True)
     uoms = fields.One2Many('product.uom', 'category', 'Unit of Measures')
 
-    def __init__(self):
-        super(UomCategory, self).__init__()
-        self._order.insert(0, ('name', 'ASC'))
-
-UomCategory()
+    @classmethod
+    def __setup__(cls):
+        super(UomCategory, cls).__setup__()
+        cls._order.insert(0, ('name', 'ASC'))
 
 
 class Uom(ModelSQL, ModelView):
     'Unit of measure'
-    _name = 'product.uom'
-    _description = __doc__
+    __name__ = 'product.uom'
     name = fields.Char('Name', size=None, required=True, states=STATES,
         translate=True, depends=DEPENDS)
     symbol = fields.Char('Symbol', size=10, required=True, states=STATES,
@@ -48,183 +47,186 @@
     digits = fields.Integer('Display Digits', required=True)
     active = fields.Boolean('Active')
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.6: corrected misspelling of ounce (was once)
         cursor.execute("UPDATE ir_model_data "\
                 "SET fs_id = REPLACE(fs_id, 'uom_once', 'uom_ounce') "\
                 "WHERE fs_id = 'uom_once' AND module = 'product'")
-        super(Uom, self).init(module_name)
+        super(Uom, cls).__register__(module_name)
 
-    def __init__(self):
-        super(Uom, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Uom, cls).__setup__()
+        cls._sql_constraints += [
             ('non_zero_rate_factor', 'CHECK((rate != 0.0) or (factor != 0.0))',
                 'Rate and factor can not be both equal to zero.')
-        ]
-        self._constraints += [
+            ]
+        cls._constraints += [
             ('check_factor_and_rate', 'invalid_factor_and_rate'),
-        ]
-        self._order.insert(0, ('name', 'ASC'))
-        self._error_messages.update({
-                'change_uom_rate_title': 'You cannot change Rate, Factor or '
+            ]
+        cls._order.insert(0, ('name', 'ASC'))
+        cls._error_messages.update({
+                'change_uom_rate_title': 'You cannot change Rate, Factor or ' \
                     'Category on a Unit of Measure. ',
-                'change_uom_rate': 'If the UOM is still not used, you can '
-                    'delete it otherwise you can deactivate it '
+                'change_uom_rate': 'If the UOM is still not used, you can ' \
+                    'delete it otherwise you can deactivate it ' \
                     'and create a new one.',
                 'invalid_factor_and_rate': 'Invalid Factor and Rate values!',
-            })
+                })
 
-    def check_xml_record(self, ids, values):
+    @classmethod
+    def __post_setup__(cls):
+        super(Uom, cls).__post_setup__()
+        if 'category' not in cls._fields:
+            print cls, cls._fields
+
+    @classmethod
+    def check_xml_record(cls, records, values):
         return True
 
-    def default_rate(self):
+    @staticmethod
+    def default_rate():
         return 1.0
 
-    def default_factor(self):
+    @staticmethod
+    def default_factor():
         return 1.0
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_rounding(self):
+    @staticmethod
+    def default_rounding():
         return 0.01
 
-    def default_digits(self):
+    @staticmethod
+    def default_digits():
         return 2
 
-    def on_change_factor(self, value):
-        if value.get('factor', 0.0) == 0.0:
+    def on_change_factor(self):
+        if (self.factor or 0.0) == 0.0:
             return {'rate': 0.0}
-        return {'rate': round(1.0 / value['factor'], self.rate.digits[1])}
+        return {
+            'rate': round(1.0 / self.factor, self.__class__.rate.digits[1]),
+            }
 
-    def on_change_rate(self, value):
-        if value.get('rate', 0.0) == 0.0:
+    def on_change_rate(self):
+        if (self.rate or 0.0) == 0.0:
             return {'factor': 0.0}
-        return {'factor': round(1.0 / value['rate'], self.factor.digits[1])}
+        return {
+            'factor': round(1.0 / self.rate, self.__class__.factor.digits[1]),
+            }
 
-    def search_rec_name(self, name, clause):
-        ids = self.search(['OR',
-            (self._rec_name,) + clause[1:],
-            ('symbol',) + clause[1:],
-            ], order=[])
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        ids = map(int, cls.search(['OR',
+                    (cls._rec_name,) + clause[1:],
+                    ('symbol',) + clause[1:],
+                    ], order=[]))
         return [('id', 'in', ids)]
 
     @staticmethod
     def round(number, precision=1.0):
         return round(number / precision) * precision
 
-    def check_factor_and_rate(self, ids):
+    def check_factor_and_rate(self):
         "Check coherence between factor and rate"
-        for uom in self.browse(ids):
-            if uom.rate == uom.factor == 0.0:
-                continue
-            if uom.rate != round(1.0 / uom.factor, self.rate.digits[1]) and \
-                    uom.factor != round(1.0 / uom.rate, self.factor.digits[1]):
-                return False
+        if self.rate == self.factor == 0.0:
+            return True
+        if (self.rate != round(
+                    1.0 / self.factor, self.__class__.rate.digits[1])
+                and self.factor != round(
+                    1.0 / self.rate, self.__class__.factor.digits[1])):
+            return False
         return True
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, uoms, values):
         if Transaction().user == 0:
-            return super(Uom, self).write(ids, values)
+            return super(Uom, cls).write(uoms, values)
         if 'rate' not in values and 'factor' not in values \
                 and 'category' not in values:
-            return super(Uom, self).write(ids, values)
+            super(Uom, cls).write(uoms, values)
+            return
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        uoms = self.browse(ids)
         old_uom = dict((uom.id, (uom.factor, uom.rate, uom.category.id)) \
                            for uom in uoms)
 
-        res = super(Uom, self).write(ids, values)
-        uoms = self.browse(ids)
+        super(Uom, cls).write(uoms, values)
 
         for uom in uoms:
             if uom.factor != old_uom[uom.id][0] \
                     or uom.rate != old_uom[uom.id][1] \
                     or uom.category.id != old_uom[uom.id][2]:
 
-                self.raise_user_error('change_uom_rate_title',
-                        error_description='change_uom_rate')
-        return res
+                cls.raise_user_error('change_uom_rate_title',
+                    error_description='change_uom_rate')
 
-    def select_accurate_field(self, uom):
+    @property
+    def accurate_field(self):
         """
         Select the more accurate field.
         It chooses the field that has the least decimal.
-
-        :param uom: a BrowseRecord of UOM.
-        :return: 'factor' or 'rate'.
         """
         lengths = {}
         for field in ('rate', 'factor'):
-            format = '%%.%df' % getattr(self, field).digits[1]
-            lengths[field] = len((format % getattr(uom,
-                field)).split('.')[1].rstrip('0'))
+            format = '%%.%df' % getattr(self.__class__, field).digits[1]
+            lengths[field] = len((format % getattr(self,
+                        field)).split('.')[1].rstrip('0'))
         if lengths['rate'] < lengths['factor']:
             return 'rate'
         elif lengths['factor'] < lengths['rate']:
             return 'factor'
-        elif uom.factor >= 1.0:
+        elif self.factor >= 1.0:
             return 'factor'
         else:
             return 'rate'
 
-    def compute_qty(self, from_uom, qty, to_uom=None, round=True):
+    @classmethod
+    def compute_qty(cls, from_uom, qty, to_uom=None, round=True):
         """
         Convert quantity for given uom's.
-
-        :param from_uom: a BrowseRecord of product.uom
-        :param qty: an int or long or float value
-        :param to_uom: a BrowseRecord of product.uom
-        :param round: a boolean to round or not the result
-        :return: the converted quantity
         """
         if not from_uom or not qty or not to_uom:
             return qty
         if from_uom.category.id != to_uom.category.id:
             return qty
-        if self.select_accurate_field(from_uom) == 'factor':
+        if from_uom.accurate_field == 'factor':
             amount = qty * from_uom.factor
         else:
             amount = qty / from_uom.rate
         if to_uom is not None:
-            if self.select_accurate_field(to_uom) == 'factor':
+            if to_uom.accurate_field == 'factor':
                 amount = amount / to_uom.factor
             else:
                 amount = amount * to_uom.rate
             if round:
-                amount = self.round(amount, to_uom.rounding)
+                amount = cls.round(amount, to_uom.rounding)
         return amount
 
-    def compute_price(self, from_uom, price, to_uom=None):
+    @classmethod
+    def compute_price(cls, from_uom, price, to_uom=None):
         """
         Convert price for given uom's.
-
-        :param from_uom: a BrowseRecord of product.uom
-        :param price: a Decimal value
-        :param to_uom: a BrowseRecord of product.uom
-        :return: the converted price
         """
         if not from_uom or not price or not to_uom:
             return price
         if from_uom.category.id != to_uom.category.id:
             return price
-        factor_format = '%%.%df' % self.factor.digits[1]
-        rate_format = '%%.%df' % self.rate.digits[1]
+        factor_format = '%%.%df' % cls.factor.digits[1]
+        rate_format = '%%.%df' % cls.rate.digits[1]
 
-        if self.select_accurate_field(from_uom) == 'factor':
+        if from_uom.accurate_field == 'factor':
             new_price = price / Decimal(factor_format % from_uom.factor)
         else:
             new_price = price * Decimal(rate_format % from_uom.rate)
 
-        if self.select_accurate_field(to_uom) == 'factor':
+        if to_uom.accurate_field == 'factor':
             new_price = new_price * Decimal(factor_format % to_uom.factor)
         else:
             new_price = new_price / Decimal(rate_format % to_uom.rate)
 
         return new_price
-
-Uom()
diff -r 69c543cd85c6 trytond/modules/product_cost_fifo/__init__.py
--- a/trytond/modules/product_cost_fifo/__init__.py	Fri Jun 08 19:34:09 2012 +0200
+++ b/trytond/modules/product_cost_fifo/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -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.
-
+from trytond.pool import Pool
 from .product import *
 from .move import *
+
+
+def register():
+    Pool.register(
+        Template,
+        Move,
+        module='product_cost_fifo', type_='model')
diff -r 69c543cd85c6 trytond/modules/product_cost_fifo/move.py
--- a/trytond/modules/product_cost_fifo/move.py	Fri Jun 08 19:34:09 2012 +0200
+++ b/trytond/modules/product_cost_fifo/move.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,73 +1,69 @@
 #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 trytond.model import ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Move']
+__metaclass__ = PoolMeta
 
-class Move(ModelSQL, ModelView):
-    _name = 'stock.move'
+
+class Move:
+    __name__ = 'stock.move'
     fifo_quantity = fields.Float('FIFO Quantity',
         digits=(16, Eval('unit_digits', 2)),
         depends=['unit_digits'])
 
-    def __init__(self):
-        super(Move, self).__init__()
-        self._sql_constraints += [
-            ('check_fifo_quantity_out',
-                'CHECK(quantity >= fifo_quantity)',
-                'FIFO quantity can not be greater than quantity!'),
-        ]
-        self._error_messages.update({
-            'del_move_fifo': 'You can not delete move that is used ' \
+    @classmethod
+    def __setup__(cls):
+        super(Move, cls).__setup__()
+        cls._sql_constraints += [
+                ('check_fifo_quantity_out',
+                    'CHECK(quantity >= fifo_quantity)',
+                    'FIFO quantity can not be greater than quantity!'),
+                ]
+        cls._error_messages.update({
+                'del_move_fifo': 'You can not delete move that is used '\
                     'for FIFO cost price!',
-            })
+                })
 
-    def default_fifo_quantity(self):
+    @staticmethod
+    def default_fifo_quantity():
         return 0.0
 
-    def _update_fifo_out_product_cost_price(self, product, quantity, uom,
+    @classmethod
+    def _update_fifo_out_product_cost_price(cls, product, quantity, uom,
             date):
         '''
         Update the product cost price of the given product. Update
         fifo_quantity on the concerned incomming moves. Return the
         cost price for outputing the given product and quantity.
-
-        :param product: a BrowseRecord of the product
-        :param quantity: the quantity of the outgoing product
-        :param uom: the uom id or a BrowseRecord of the uom
-        :param date: the date for the currency rate calculation
-        :return: cost_price (of type decimal)
         '''
 
-        template_obj = Pool().get('product.template')
-        uom_obj = Pool().get('product.uom')
+        Uom = Pool().get('product.uom')
 
-        if isinstance(uom, (int, long)):
-            uom = uom_obj.browse(uom)
-
-        total_qty = uom_obj.compute_qty(uom, quantity, product.default_uom,
+        total_qty = Uom.compute_qty(uom, quantity, product.default_uom,
                 round=False)
 
-        fifo_moves = template_obj.get_fifo_move(product.template.id, total_qty)
+        fifo_moves = product.template.get_fifo_move(total_qty)
 
         cost_price = Decimal("0.0")
         consumed_qty = 0.0
         for move, move_qty in fifo_moves:
             consumed_qty += move_qty
 
-            move_unit_price = uom_obj.compute_price(move.uom, move.unit_price,
+            move_unit_price = Uom.compute_price(move.uom, move.unit_price,
                     move.product.default_uom)
             cost_price += move_unit_price * Decimal(str(move_qty))
 
-            self._update_product_cost_price(product.id, -move_qty,
+            cls._update_product_cost_price(product.id, -move_qty,
                     product.default_uom, move_unit_price, move.currency,
                     move.company, date)
 
-            move_qty = uom_obj.compute_qty(product.default_uom, move_qty,
+            move_qty = Uom.compute_qty(product.default_uom, move_qty,
                     move.uom, round=False)
-            self.write(move.id, {
+            cls.write([move], {
                 'fifo_quantity': (move.fifo_quantity or 0.0) + move_qty,
                 })
 
@@ -75,71 +71,71 @@
             cost_price = cost_price / Decimal(str(consumed_qty))
 
         if cost_price != Decimal("0"):
-            digits = self.cost_price.digits
+            digits = cls.cost_price.digits
             return cost_price.quantize(
                 Decimal(str(10.0 ** -digits[1])))
         else:
             return product.cost_price
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         pool = Pool()
-        location_obj = pool.get('stock.location')
-        product_obj = pool.get('product.product')
-        date_obj = pool.get('ir.date')
+        Location = pool.get('stock.location')
+        Product = pool.get('product.product')
+        Date = pool.get('ir.date')
 
-        today = date_obj.today()
+        today = Date.today()
         effective_date = vals.get('effective_date') or today
 
         if vals.get('state') == 'done':
-            from_location = location_obj.browse(vals['from_location'])
-            to_location = location_obj.browse(vals['to_location'])
-            product = product_obj.browse(vals['product'])
+            from_location = Location(vals['from_location'])
+            to_location = Location(vals['to_location'])
+            product = Product(vals['product'])
             if (from_location.type in ('supplier', 'production')
                     and to_location.type == 'storage'
                     and product.cost_price_method == 'fifo'):
-                self._update_product_cost_price(vals['product'],
+                cls._update_product_cost_price(vals['product'],
                         vals['quantity'], vals['uom'], vals['unit_price'],
                         vals['currency'], effective_date)
             if (to_location.type == 'supplier'
                     and from_location.type == 'storage'
                     and product.cost_price_method == 'fifo'):
-                self._update_product_cost_price(vals['product'],
+                cls._update_product_cost_price(vals['product'],
                         -vals['quantity'], vals['uom'], vals['unit_price'],
                         vals['currency'], effective_date)
             if to_location.type != 'storage' \
                     and to_location.type != 'supplier' \
                     and product.cost_price_method == 'fifo':
 
-                cost_price = self._update_fifo_out_product_cost_price(product,
+                cost_price = cls._update_fifo_out_product_cost_price(product,
                         vals['quantity'], vals['uom'], effective_date)
                 if not vals.get('cost_price'):
                     vals['cost_price'] = cost_price
 
-        return super(Move, self).create(vals)
+        return super(Move, cls).create(vals)
 
-    def write(self, ids, vals):
-        date_obj = Pool().get('ir.date')
+    @classmethod
+    def write(cls, moves, vals):
+        Date = Pool().get('ir.date')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        today = date_obj.today()
+        today = Date.today()
         effective_date = vals.get('effective_date') or today
 
         if 'state' in vals and vals['state'] == 'done':
-            for move in self.browse(ids):
+            for move in moves:
                 if vals['state'] == 'cancel':
                     if (move.from_location.type in ('supplier', 'production')
                             and move.to_location.type == 'storage'
                             and move.state != 'cancel'
                             and move.product.cost_price_method == 'fifo'):
-                        self._update_product_cost_price(move.product.id,
+                        cls._update_product_cost_price(move.product.id,
                                 -move.quantity, move.uom, move.unit_price,
                                 move.currency, move.company, effective_date)
                     if (move.to_location.type == 'supplier'
                             and move.from_location.type == 'storage'
                             and move.state != 'cancel'
                             and move.product.cost_price_method == 'fifo'):
-                        self._update_product_cost_price(move.product.id,
+                        cls._update_product_cost_price(move.product.id,
                                 move.quantity, move.uom, move.unit_price,
                                 move.currency, move.company, effective_date)
 
@@ -148,31 +144,31 @@
                             and move.to_location.type == 'storage'
                             and move.state != 'done'
                             and move.product.cost_price_method == 'fifo'):
-                        self._update_product_cost_price(move.product.id,
+                        cls._update_product_cost_price(move.product.id,
                                 move.quantity, move.uom, move.unit_price,
                                 move.currency, move.company, effective_date)
                     if (move.to_location.type == 'supplier'
                             and move.from_location.type == 'storage'
                             and move.state != 'done'
                             and move.product.cost_price_method == 'fifo'):
-                        self._update_product_cost_price(move.product.id,
+                        cls._update_product_cost_price(move.product.id,
                                 -move.quantity, move.uom, move.unit_price,
                                 move.currency, move.company, effective_date)
                     if move.to_location.type != 'storage' \
                             and move.to_location.type != 'supplier' \
                             and move.product.cost_price_method == 'fifo':
-                        cost_price = self._update_fifo_out_product_cost_price(
+                        cost_price = cls._update_fifo_out_product_cost_price(
                                 move.product, move.quantity, move.uom,
                                 effective_date)
                         if not vals.get('cost_price'):
                             vals['cost_price'] = cost_price
-        return super(Move, self).write(ids, vals)
+        super(Move, cls).write(moves, vals)
 
-    def delete(self, ids):
-        if self.search([
-            ('id', 'in', ids),
-            ('fifo_quantity', '!=', 0.0),
-            ]):
-            self.raise_user_error('del_move_fifo')
-        return super(Move, self).delete(ids)
-Move()
+    @classmethod
+    def delete(cls, moves):
+        if cls.search([
+                ('id', 'in', [m.id for m in moves]),
+                ('fifo_quantity', '!=', 0.0),
+                ]):
+            cls.raise_user_error('del_move_fifo')
+        super(Move, cls).delete(moves)
diff -r 69c543cd85c6 trytond/modules/product_cost_fifo/product.py
--- a/trytond/modules/product_cost_fifo/product.py	Fri Jun 08 19:34:09 2012 +0200
+++ b/trytond/modules/product_cost_fifo/product.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,66 +1,61 @@
 #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 copy
 import datetime
-from trytond.model import ModelView, ModelSQL
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Template']
+__metaclass__ = PoolMeta
 
-class Template(ModelSQL, ModelView):
-    _name = "product.template"
 
-    def __init__(self):
-        super(Template, self).__init__()
+class Template:
+    __name__ = 'product.template'
+
+    @classmethod
+    def __setup__(cls):
+        super(Template, cls).__setup__()
         new_sel = ('fifo', 'FIFO')
-        if new_sel not in self.cost_price_method.selection:
-            self.cost_price_method = copy.copy(self.cost_price_method)
-            self.cost_price_method.selection.append(new_sel)
-            self._reset_columns()
+        if new_sel not in cls.cost_price_method.selection:
+            cls.cost_price_method.selection.append(new_sel)
 
-    def get_fifo_move(self, template_id, quantity=0.0):
+    def get_fifo_move(self, quantity=0.0):
         '''
         Return a list of (move, qty) where move is the move to be
         consumed and qty is the quantity (in the product default uom)
         to be consumed on this move. The list contains the "first in"
         moves for the given quantity.
-
-        :param template_id: the product template id
-        :param quantity: the quantity to be removed from the stock
-        :return: list of (move, qty) where move is a browse record,
-        qty is a float
         '''
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        uom_obj = pool.get('product.uom')
-        location_obj = pool.get('stock.location')
+        Move = pool.get('stock.move')
+        Uom = pool.get('product.uom')
+        Location = pool.get('stock.location')
 
-        locations = location_obj.search([
+        locations = Location.search([
             ('type', '=', 'storage'),
             ])
         stock_date_end = datetime.date.today()
-
-        with Transaction().set_context(locations=locations,
+        location_ids = [l.id for l in locations]
+        with Transaction().set_context(locations=location_ids,
                 stock_date_end=stock_date_end):
-            template = self.browse(template_id)
+            template = Template(self.id)
         offset = 0
         limit = Transaction().cursor.IN_MAX
         avail_qty = template.quantity
         fifo_moves = []
 
         while avail_qty > 0.0:
-            move_ids = move_obj.search([
+            moves = Move.search([
                 ('product.template.id', '=', template.id),
                 ('state', '=', 'done'),
                 ('from_location.type', '=', 'supplier'),
                 ], offset=offset, limit=limit,
                 order=[('effective_date', 'DESC'), ('id', 'DESC')])
-            if not move_ids:
+            if not moves:
                 break
             offset += limit
 
-            for move in move_obj.browse(move_ids):
-                qty = uom_obj.compute_qty(move.uom,
+            for move in moves:
+                qty = Uom.compute_qty(move.uom,
                         move.quantity - move.fifo_quantity,
                         template.default_uom, round=False)
                 avail_qty -= qty
@@ -76,5 +71,3 @@
 
         fifo_moves.reverse()
         return fifo_moves
-
-Template()
diff -r 38fae63d7afa trytond/modules/product_cost_history/__init__.py
--- a/trytond/modules/product_cost_history/__init__.py	Fri Jun 08 19:34:10 2012 +0200
+++ b/trytond/modules/product_cost_history/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,5 +1,16 @@
 #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.pool import Pool
 from .ir import *
 from .product import *
+
+
+def register():
+    Pool.register(
+        Property,
+        ProductCostHistory,
+        module='product_cost_history', type_='model')
+    Pool.register(
+        OpenProductCostHistory,
+        module='product_cost_history', type_='wizard')
diff -r 38fae63d7afa trytond/modules/product_cost_history/ir.py
--- a/trytond/modules/product_cost_history/ir.py	Fri Jun 08 19:34:10 2012 +0200
+++ b/trytond/modules/product_cost_history/ir.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,10 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from trytond.model import ModelView, ModelSQL
+from trytond.pool import PoolMeta
 
+__all__ = ['Property']
+__metaclass__ = PoolMeta
 
-class Property(ModelSQL, ModelView):
-    _name = 'ir.property'
+
+class Property:
+    __name__ = 'ir.property'
     _history = True
-
-Property()
diff -r 38fae63d7afa trytond/modules/product_cost_history/product.py
--- a/trytond/modules/product_cost_history/product.py	Fri Jun 08 19:34:10 2012 +0200
+++ b/trytond/modules/product_cost_history/product.py	Mon Sep 24 20:36:41 2012 +0200
@@ -6,24 +6,27 @@
 from trytond.pool import Pool
 from trytond.transaction import Transaction
 
+__all__ = ['ProductCostHistory', 'OpenProductCostHistory']
+
 
 class ProductCostHistory(ModelSQL, ModelView):
     'History of Product Cost'
-    _name = 'product.product.cost_history'
-    _description = __doc__
+    __name__ = 'product.product.cost_history'
     _rec_name = 'date'
-
     template = fields.Many2One('product.template', 'Product')
     date = fields.DateTime('Date')
     cost_price = fields.Numeric('Cost Price')
 
-    def __init__(self):
-        super(ProductCostHistory, self).__init__()
-        self._order.insert(0, ('date', 'DESC'))
+    @classmethod
+    def __setup__(cls):
+        super(ProductCostHistory, cls).__setup__()
+        cls._order.insert(0, ('date', 'DESC'))
 
-    def table_query(self):
-        property_obj = Pool().get('ir.property')
-        field_obj = Pool().get('ir.model.field')
+    @staticmethod
+    def table_query():
+        pool = Pool()
+        Property = pool.get('ir.property')
+        Field = pool.get('ir.model.field')
         return ('SELECT '
                     'MAX(h.__id) AS id, '
                     'MAX(h.create_uid) AS create_uid, '
@@ -34,38 +37,33 @@
                     'CAST(TRIM(\',\' FROM SUBSTRING(h.res FROM \',.*\')) AS '
                         'INTEGER) AS template, '
                     'CAST(TRIM(\',\' FROM h.value) AS NUMERIC) AS cost_price '
-                'FROM "' + property_obj._table + '__history" h '
-                    'JOIN "' + field_obj._table + '" f ON (f.id = h.field) '
+                'FROM "' + Property._table + '__history" h '
+                    'JOIN "' + Field._table + '" f ON (f.id = h.field) '
                 'WHERE f.name = \'cost_price\' '
                     'AND h.res LIKE \'product.template,%%\' '
                 'GROUP BY h.id, COALESCE(h.write_date, h.create_date), h.res, '
                     'h.value',
                 [])
 
-ProductCostHistory()
-
 
 class OpenProductCostHistory(Wizard):
     'Open Product Cost History'
-    _name = 'product.product.cost_history.open'
+    __name__ = 'product.product.cost_history.open'
     start_state = 'open'
-
     open = StateAction('product_cost_history.act_product_cost_history_form')
 
-    def do_open(self, session, action):
+    def do_open(self, action):
         pool = Pool()
-        product_obj = pool.get('product.product')
+        Product = pool.get('product.product')
 
         active_id = Transaction().context.get('active_id')
         if not active_id or active_id < 0:
             action['pyson_domain'] = PYSONEncoder().encode([
-                ('template', '=', False),
-            ])
+                    ('template', '=', None),
+                    ])
         else:
-            product = product_obj.browse(active_id)
+            product = Product(active_id)
             action['pyson_domain'] = PYSONEncoder().encode([
-                ('template', '=', product.template.id),
-            ])
+                    ('template', '=', product.template.id),
+                    ])
         return action, {}
-
-OpenProductCostHistory()
diff -r bb71ef821e32 trytond/modules/product_measurements/__init__.py
--- a/trytond/modules/product_measurements/__init__.py	Fri Jun 08 19:34:11 2012 +0200
+++ b/trytond/modules/product_measurements/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,4 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from product import *
+
+
+def register():
+    Pool.register(
+        Template,
+        module='product_measurements', type_='model')
diff -r bb71ef821e32 trytond/modules/product_measurements/product.py
--- a/trytond/modules/product_measurements/product.py	Fri Jun 08 19:34:11 2012 +0200
+++ b/trytond/modules/product_measurements/product.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,14 +1,17 @@
 #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 Model, fields
+from trytond.model import fields
 from trytond.pyson import Eval, Bool, Id
-from trytond.pool import Pool
+from trytond.pool import PoolMeta
+
+__all__ = ['Template']
+__metaclass__ = PoolMeta
 
 NON_MEASURABLE = ['service']
 
 
-class Template(Model):
-    _name = 'product.template'
+class Template:
+    __name__ = 'product.template'
 
     length = fields.Float('Length',
         digits=(16, Eval('length_digits', 2)),
@@ -24,7 +27,7 @@
             },
         depends=['type', 'length'])
     length_digits = fields.Function(fields.Integer('Length Digits',
-            on_change_with=['length_uom']), 'get_field_digits')
+            on_change_with=['length_uom']), 'on_change_with_length_digits')
     height = fields.Float('Height',
         digits=(16, Eval('height_digits', 2)),
         states={
@@ -39,7 +42,7 @@
             },
         depends=['type', 'height'])
     height_digits = fields.Function(fields.Integer('Height Digits',
-            on_change_with=['height_uom']), 'get_field_digits')
+            on_change_with=['height_uom']), 'on_change_with_height_digits')
     width = fields.Float('Width',
         digits=(16, Eval('width_digits', 2)),
         states={
@@ -54,7 +57,7 @@
             },
         depends=['type', 'width'])
     width_digits = fields.Function(fields.Integer('Width Digits',
-            on_change_with=['width_uom']), 'get_field_digits')
+            on_change_with=['width_uom']), 'on_change_with_width_digits')
     weight = fields.Float('Weight',
         digits=(16, Eval('weight_digits', 2)),
         states={
@@ -69,48 +72,36 @@
             },
         depends=['type', 'weight'])
     weight_digits = fields.Function(fields.Integer('Weight Digits',
-            on_change_with=['weight_uom']), 'get_field_digits')
+            on_change_with=['weight_uom']), 'on_change_with_weight_digits')
 
-    def _on_change_with_field_digits(self, uom_id):
-        uom_obj = Pool().get('product.uom')
-        if uom_id:
-            uom = uom_obj.browse(uom_id)
-            return uom.digits
+    def on_change_with_length_digits(self, name=None):
+        return (self.length_uom.digits if self.length_uom
+            else self.default_length_digits())
+
+    @staticmethod
+    def default_length_digits():
         return 2
 
-    def on_change_with_length_digits(self, values):
-        return self._on_change_with_field_digits(values.get('length_uom'))
+    def on_change_with_height_digits(self, name=None):
+        return (self.height_uom.digits if self.height_uom
+            else self.default_height_digits())
 
-    def default_length_digits(self):
+    @staticmethod
+    def default_height_digits():
         return 2
 
-    def on_change_with_height_digits(self, values):
-        return self._on_change_with_field_digits(values.get('height_uom'))
+    def on_change_with_width_digits(self, name=None):
+        return (self.width_uom.digits if self.width_uom
+            else self.default_width_digits())
 
-    def default_height_digits(self):
+    @staticmethod
+    def default_width_digits():
         return 2
 
-    def on_change_with_width_digits(self, values):
-        return self._on_change_with_field_digits(values.get('width_uom'))
+    def on_change_with_weight_digits(self, name=None):
+        return (self.weight_uom.digits if self.weight_uom
+            else self.default_weight_digits())
 
-    def default_width_digits(self):
+    @staticmethod
+    def default_weight_digits():
         return 2
-
-    def on_change_with_weight_digits(self, values):
-        return self._on_change_with_field_digits(values.get('weight_uom'))
-
-    def default_weight_digits(self):
-        return 2
-
-    def get_field_digits(self, ids, name):
-        digits = {}
-        field = '%s_uom' % name[:-7]
-        for template in self.browse(ids):
-            uom = getattr(template, field)
-            if uom:
-                digits[template.id] = uom.digits
-            else:
-                digits[template.id] = 2
-        return digits
-
-Template()
diff -r 625672ac7f09 trytond/modules/product_price_list/__init__.py
--- a/trytond/modules/product_price_list/__init__.py	Fri Jun 08 19:34:13 2012 +0200
+++ b/trytond/modules/product_price_list/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,4 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+from trytond.pool import Pool
+from .price_list import *
 
-from .price_list import *
+
+def register():
+    Pool.register(
+        PriceList,
+        PriceListLine,
+        module='product_price_list', type_='model')
diff -r 625672ac7f09 trytond/modules/product_price_list/price_list.py
--- a/trytond/modules/product_price_list/price_list.py	Fri Jun 08 19:34:13 2012 +0200
+++ b/trytond/modules/product_price_list/price_list.py	Mon Sep 24 20:36:41 2012 +0200
@@ -10,6 +10,8 @@
 from trytond.pool import Pool
 from trytond.backend import TableHandler
 
+__all__ = ['PriceList', 'PriceListLine']
+
 
 # code snippet taken from http://docs.python.org/library/tokenize.html
 def decistmt(s):
@@ -44,8 +46,8 @@
 
 class PriceList(ModelSQL, ModelView):
     'Price List'
-    _name = 'product.price_list'
-    _description = __doc__
+    __name__ = 'product.price_list'
+
     name = fields.Char('Name', required=True, translate=True)
     company = fields.Many2One('company.company', 'Company', required=True,
         select=True, domain=[
@@ -54,7 +56,8 @@
             ])
     lines = fields.One2Many('product.price_list.line', 'price_list', 'Lines')
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
     def _get_context_price_list_line(self, party, product, unit_price,
@@ -74,68 +77,43 @@
             'unit_price': unit_price,
         }
 
-    def compute(self, price_list, party, product, unit_price, quantity, uom,
+    def compute(self, party, product, unit_price, quantity, uom,
             pattern=None):
         '''
         Compute price based on price list of party
 
-        :param price_list: the price list id or the BrowseRecord of the
-            product.price_list
-        :param party: the party id or the BrowseRecord of the party.party
-        :param product: the product id or the BrowseRecord of the
-            product.product
         :param unit_price: a Decimal for the default unit price in the
             company's currency and default uom of the product
         :param quantity: the quantity of product
-        :param uom: the UOM id or the BrowseRecord of the product.uom
+        :param uom: a instance of the product.uom
         :param pattern: a dictionary with price list field as key
             and match value as value
         :return: the computed unit price
         '''
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
-        price_list_line_obj = pool.get('product.price_list.line')
 
-        if not price_list:
-            return unit_price
-
-        if isinstance(price_list, (int, long)):
-            price_list = self.browse(price_list)
-
-        if isinstance(party, (int, long)):
-            party = party_obj.browse(party)
-
-        if isinstance(product, (int, long)):
-            product = product_obj.browse(product)
-
-        if isinstance(uom, (int, long)):
-            uom = uom_obj.browse(uom)
+        Uom = Pool().get('product.uom')
 
         if pattern is None:
             pattern = {}
 
         pattern = pattern.copy()
         pattern['product'] = product and product.id or None
-        pattern['quantity'] = uom_obj.compute_qty(uom, quantity,
+        pattern['quantity'] = Uom.compute_qty(uom, quantity,
                 product.default_uom, round=False)
 
-        for line in price_list.lines:
-            if price_list_line_obj.match(line, pattern):
+        for line in self.lines:
+            if line.match(pattern):
                 with Transaction().set_context(
                         self._get_context_price_list_line(party, product,
                             unit_price, quantity, uom)):
-                    return price_list_line_obj.get_unit_price(line)
+                    return line.get_unit_price(line)
         return unit_price
 
-PriceList()
-
 
 class PriceListLine(ModelSQL, ModelView):
     'Price List Line'
-    _name = 'product.price_list.line'
-    _description = __doc__
+    __name__ = 'product.price_list.line'
+
     price_list = fields.Many2One('product.price_list', 'Price List',
             required=True, ondelete='CASCADE')
     product = fields.Many2One('product.product', 'Product')
@@ -145,105 +123,91 @@
     quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)),
             depends=['unit_digits'])
     unit_digits = fields.Function(fields.Integer('Unit Digits',
-        on_change_with=['product']), 'get_unit_digits')
+        on_change_with=['product']), 'on_change_with_unit_digits')
     formula = fields.Char('Formula', required=True,
             help='Python expression that will be evaluated with:\n' \
                     '- unit_price: the original unit_price')
 
-    def __init__(self):
-        super(PriceListLine, self).__init__()
-        self._order.insert(0, ('price_list', 'ASC'))
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._constraints += [
-            ('check_formula', 'invalid_formula'),
-        ]
-        self._error_messages.update({
-            'invalid_formula': 'Invalid formula!',
-        })
+    @classmethod
+    def __setup__(cls):
+        super(PriceListLine, cls).__setup__()
+        cls._order.insert(0, ('price_list', 'ASC'))
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._constraints += [
+                ('check_formula', 'invalid_formula'),
+                ]
+        cls._error_messages.update({
+                'invalid_formula': 'Invalid formula!',
+                })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(PriceListLine, self).init(module_name)
+        super(PriceListLine, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_formula(self):
+    @staticmethod
+    def default_formula():
         return 'unit_price'
 
-    def on_change_with_unit_digits(self, vals):
-        product_obj = Pool().get('product.product')
-        if vals.get('product'):
-            product = product_obj.browse(vals['product'])
-            return product.default_uom.digits
+    def on_change_with_unit_digits(self, name=None):
+        if self.product:
+            return self.product.default_uom.digits
         return 2
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            if line.product:
-                res[line.id] = line.product.default_uom.digits
-            else:
-                res[line.id] = 2
-        return res
-
-    def check_formula(self, ids):
+    def check_formula(self):
         '''
         Check formula
         '''
-        price_list_obj = Pool().get('product.price_list')
-        context = price_list_obj._get_context_price_list_line(None, None,
+        pool = Pool()
+        PriceList = pool.get('product.price_list')
+        context = PriceList()._get_context_price_list_line(None, None,
                 Decimal('0.0'), 0, None)
-        lines = self.browse(ids)
+
         with Transaction().set_context(**context):
-            for line in lines:
-                try:
-                    if not isinstance(self.get_unit_price(line), Decimal):
-                        return False
-                except Exception:
+            try:
+                if not isinstance(self.get_unit_price(), Decimal):
                     return False
+            except Exception:
+                return False
         return True
 
-    def match(self, line, pattern):
+    def match(self, pattern):
         '''
         Match line on pattern
 
-        :param line: a BrowseRecord of price list line
         :param pattern: a dictonary with price list line field as key
                 and match value as value
         :return: a boolean
         '''
         res = True
         for field in pattern.keys():
-            if field not in self._columns:
+            if field not in self._fields:
                 continue
-            if not line[field]:
+            if not getattr(self, field):
                 continue
-            if self._columns[field]._type == 'many2one':
-                if line[field].id != pattern[field]:
+            if self._fields[field]._type == 'many2one':
+                if getattr(self, field).id != pattern[field]:
                     res = False
                     break
             elif field == 'quantity':
-                if line[field] > pattern[field]:
+                if getattr(self, field) > pattern[field]:
                     res = False
                     break
             else:
-                if line[field] != pattern[field]:
+                if getattr(self, field) != pattern[field]:
                     res = False
                     break
         return res
 
-    def get_unit_price(self, line):
+    def get_unit_price(self):
         '''
-        Return unit price for a line
-
-        :param line: a BrowseRecord of price list line
-        :return: a Decimal
+        Return unit price (as Decimal)
         '''
         context = Transaction().context.copy()
         context['Decimal'] = Decimal
-        return safe_eval(decistmt(line.formula), context)
-
-PriceListLine()
+        return safe_eval(decistmt(self.formula), context)
diff -r 98a16ccdf418 trytond/modules/production/__init__.py
--- a/trytond/modules/production/__init__.py	Mon Sep 10 15:28:23 2012 +0200
+++ b/trytond/modules/production/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,8 +1,31 @@
 #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.pool import Pool
 from .configuration import *
 from .bom import *
 from .product import *
 from .production import *
 from .stock import *
+
+
+def register():
+    Pool.register(
+        Configuration,
+        BOM,
+        BOMInput,
+        BOMOutput,
+        BOMTree,
+        OpenBOMTreeStart,
+        OpenBOMTreeTree,
+        Production,
+        AssignFailed,
+        Product,
+        ProductBom,
+        Location,
+        Move,
+        module='production', type_='model')
+    Pool.register(
+        Assign,
+        OpenBOMTree,
+        module='production', type_='wizard')
diff -r 98a16ccdf418 trytond/modules/production/bom.py
--- a/trytond/modules/production/bom.py	Mon Sep 10 15:28:23 2012 +0200
+++ b/trytond/modules/production/bom.py	Mon Sep 24 20:36:41 2012 +0200
@@ -7,10 +7,13 @@
 from trytond.pool import Pool
 
 
+__all__ = ['BOM', 'BOMInput', 'BOMOutput', 'BOMTree', 'OpenBOMTreeStart',
+    'OpenBOMTreeTree', 'OpenBOMTree']
+
+
 class BOM(ModelSQL, ModelView):
     "Bill of Material"
-    _name = 'production.bom'
-    _description = __doc__
+    __name__ = 'production.bom'
 
     name = fields.Char('Name', required=True, translate=True)
     active = fields.Boolean('Active', select=1)
@@ -19,35 +22,34 @@
     output_products = fields.Many2Many('production.bom.output',
         'bom', 'product', 'Output Products')
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def compute_factor(self, bom, product, quantity, uom):
+    def compute_factor(self, product, quantity, uom):
         '''
         Compute factor for an output product
         '''
-        uom_obj = Pool().get('product.uom')
-        for output in bom.outputs:
+        Uom = Pool().get('product.uom')
+        for output in self.outputs:
             if output.product == product:
-                quantity = uom_obj.compute_qty(uom, quantity,
+                quantity = Uom.compute_qty(uom, quantity,
                     output.uom, round=False)
                 return quantity / output.quantity
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, records, default=None):
         if default is None:
             default = {}
         else:
             default = default.copy()
         default['output_products'] = False
-        return super(BOM, self).copy(ids, default=default)
-
-BOM()
+        return super(BOM, cls).copy(records, default=default)
 
 
 class BOMInput(ModelSQL, ModelView):
     "Bill of Material Input"
-    _name = 'production.bom.input'
-    _description = __doc__
+    __name__ = 'production.bom.input'
     _rec_name = 'product'
 
     bom = fields.Many2One('production.bom', 'BOM', required=True,
@@ -58,104 +60,75 @@
         ], on_change=['product', 'uom'])
     uom_category = fields.Function(fields.Many2One(
         'product.uom.category', 'Uom Category',
-        on_change_with=['product']), 'get_uom_category')
+        on_change_with=['product']), 'on_change_with_uom_category')
     uom = fields.Many2One('product.uom', 'Uom', required=True,
         domain=[
             ('category', '=', Eval('uom_category')),
         ], depends=['uom_category'])
     unit_digits = fields.Function(fields.Integer('Unit Digits',
-        on_change_with=['uom']), 'get_unit_digits')
+        on_change_with=['uom']), 'on_change_with_unit_digits')
     quantity = fields.Float('Quantity', required=True,
         digits=(16, Eval('unit_digits', 2)),
         depends=['unit_digits'])
 
-    def __init__(self):
-        super(BOMInput, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(BOMInput, cls).__setup__()
+        cls._sql_constraints = [
             ('product_bom_uniq', 'UNIQUE(product, bom)',
                 'product_bom_uniq'),
-        ]
-        self._constraints += [
+            ]
+        cls._constraints += [
             ('check_bom_recursion', 'recursive_bom'),
-        ]
-        self._error_messages.update({
-            'product_bom_uniq': 'Product must be unique per BOM!',
-            'recursive_bom': 'You can not create recursive BOMs!',
-        })
+            ]
+        cls._error_messages.update({
+                'product_bom_uniq': 'Product must be unique per BOM!',
+                'recursive_bom': 'You can not create recursive BOMs!',
+                })
 
-    def on_change_product(self, vals):
-        product_obj = Pool().get('product.product')
-
+    def on_change_product(self):
         res = {}
-        if vals.get('product'):
-            product = product_obj.browse(vals['product'])
-            uom_ids = [x.id for x in product.default_uom.category.uoms]
-            if (not vals.get('uom')
-                    or vals.get('uom') not in uom_ids):
-                res['uom'] = product.default_uom.id
-                res['uom.rec_name'] = product.default_uom.rec_name
-                res['unit_digits'] = product.default_uom.digits
+        if self.product:
+            uoms = self.product.default_uom.category.uoms
+            if (not self.uom or self.uom not in uoms):
+                res['uom'] = self.product.default_uom.id
+                res['uom.rec_name'] = self.product.default_uom.rec_name
+                res['unit_digits'] = self.product.default_uom.digits
         else:
-            res['uom'] = False
+            res['uom'] = None
             res['uom.rec_name'] = ''
             res['unit_digits'] = 2
         return res
 
-    def on_change_with_uom_category(self, vals):
-        product_obj = Pool().get('product.product')
-        if vals.get('product'):
-            product = product_obj.browse(vals['product'])
-            return product.default_uom.category.id
-        return False
+    def on_change_with_uom_category(self, name=None):
+        if self.product:
+            return self.product.default_uom.category.id
 
-    def get_uom_category(self, ids, name):
-        res = {}
-        for input in self.browse(ids):
-            res[input.id] = input.product.default_uom.category.id
-        return res
-
-    def on_change_with_unit_digits(self, vals):
-        uom_obj = Pool().get('product.uom')
-        if vals.get('uom'):
-            uom = uom_obj.browse(vals['uom'])
-            return uom.digits
+    def on_change_with_unit_digits(self, name=None):
+        if self.uom:
+            return self.uom.digits
         return 2
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for input in self.browse(ids):
-            res[input.id] = input.uom.digits
-        return res
-
-    def check_bom_recursion(self, ids):
+    def check_bom_recursion(self):
         '''
         Check BOM recursion
         '''
-        product_obj = Pool().get('product.product')
+        return self.product.check_bom_recursion()
 
-        inputs = self.browse(ids)
-        product_ids = [input.product.id for input in inputs]
-        return product_obj.check_bom_recursion(product_ids)
-
-    def compute_quantity(self, line, factor):
-        uom_obj = Pool().get('product.uom')
-        return uom_obj.round(line.quantity * factor, line.uom.rounding)
-
-BOMInput()
+    def compute_quantity(self, factor):
+        Uom = Pool().get('product.uom')
+        return Uom.round(self.quantity * factor, self.uom.rounding)
 
 
 class BOMOutput(BOMInput):
     "Bill of Material Output"
-    _name = 'production.bom.output'
-    _description = __doc__
-
-BOMOutput()
+    __name__ = 'production.bom.output'
+    _table = 'production_bom_output'  # Needed to override BOMInput._table
 
 
 class BOMTree(ModelView):
     'BOM Tree'
-    _name = 'production.bom.tree'
-    _description = __doc__
+    __name__ = 'production.bom.tree'
 
     product = fields.Many2One('product.product', 'Product')
     quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)),
@@ -164,9 +137,9 @@
     unit_digits = fields.Integer('Unit Digits')
     childs = fields.One2Many('production.bom.tree', None, 'Childs')
 
-    def tree(self, product, quantity, uom, bom=None):
-        bom_obj = Pool().get('production.bom')
-        input_obj = Pool().get('production.bom.input')
+    @classmethod
+    def tree(cls, product, quantity, uom, bom=None):
+        Input = Pool().get('production.bom.input')
 
         result = []
         if bom is None:
@@ -174,26 +147,24 @@
                 return result
             bom = product.boms[0].bom
 
-        factor = bom_obj.compute_factor(bom, product, quantity, uom)
-        for input in bom.inputs:
-            quantity = input_obj.compute_quantity(input, factor)
-            childs = self.tree(input.product, quantity, input.uom)
+        factor = bom.compute_factor(product, quantity, uom)
+        for input_ in bom.inputs:
+            quantity = Input.compute_quantity(input_, factor)
+            childs = cls.tree(input_.product, quantity, input_.uom)
             values = {
-                'product': input.product.id,
+                'product': input_.product.id,
                 'quantity': quantity,
-                'uom': input.uom.id,
-                'unit_digits': input.uom.digits,
+                'uom': input_.uom.id,
+                'unit_digits': input_.uom.digits,
                 'childs': childs,
             }
             result.append(values)
         return result
 
-BOMTree()
-
 
 class OpenBOMTreeStart(ModelView):
     'Open BOM Tree'
-    _name = 'production.bom.tree.open.start'
+    __name__ = 'production.bom.tree.open.start'
 
     quantity = fields.Float('Quantity', required=True,
         digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
@@ -211,44 +182,39 @@
         ], depends=['product'])
     product = fields.Many2One('product.product', 'Product', readonly=True)
 
-    def on_change_with_unit_digits(self, values):
-        uom_obj = Pool().get('product.uom')
-        if values.get('uom'):
-            uom = uom_obj.browse(values['uom'])
-            return uom.digits
+    def on_change_with_unit_digits(self):
+        if self.uom:
+            return self.uom.digits
         return 2
 
-OpenBOMTreeStart()
-
 
 class OpenBOMTreeTree(ModelView):
     'Open BOM Tree'
-    _name = 'production.bom.tree.open.tree'
+    __name__ = 'production.bom.tree.open.tree'
 
     bom_tree = fields.One2Many('production.bom.tree', None, 'BOM Tree')
 
-    def tree(self, bom, product, quantity, uom):
+    @classmethod
+    def tree(cls, bom, product, quantity, uom):
         pool = Pool()
-        tree_obj = pool.get('production.bom.tree')
+        Tree = pool.get('production.bom.tree')
 
-        childs = tree_obj.tree(product, quantity, uom, bom=bom)
+        childs = Tree.tree(product, quantity, uom, bom=bom)
         bom_tree = [{
-            'product': product.id,
-            'quantity': quantity,
-            'uom': uom.id,
-            'unit_digits': uom.digits,
-            'childs': childs,
-        }]
+                'product': product.id,
+                'quantity': quantity,
+                'uom': uom.id,
+                'unit_digits': uom.digits,
+                'childs': childs,
+                }]
         return {
             'bom_tree': bom_tree,
-        }
-
-OpenBOMTreeTree()
+            }
 
 
 class OpenBOMTree(Wizard):
     'Open BOM Tree'
-    _name = 'production.bom.tree.open'
+    __name__ = 'production.bom.tree.open'
 
     start = StateView('production.bom.tree.open.start',
         'production.bom_tree_open_start_view_form', [
@@ -261,29 +227,27 @@
             Button('Close', 'end', 'tryton-close'),
             ])
 
-    def default_start(self, session, fields):
-        product_obj = Pool().get('product.product')
+    def default_start(self, fields):
+        Product = Pool().get('product.product')
         defaults = {}
-        product = product_obj.browse(Transaction().context['active_id'])
+        product = Product(Transaction().context['active_id'])
         defaults['category'] = product.default_uom.category.id
-        if session.start.uom:
-            defaults['uom'] = session.start.uom.id
-            defaults['unit_digits'] = session.start.unit_digits
+        if getattr(self.start, 'uom', None):
+            defaults['uom'] = self.start.uom.id
+            defaults['unit_digits'] = self.start.unit_digits
         else:
             defaults['uom'] = product.default_uom.id
             defaults['unit_digits'] = product.default_uom.digits
         defaults['product'] = product.id
-        if session.start.bom:
-            defaults['bom'] = session.start.bom.id
+        if getattr(self.start, 'bom', None):
+            defaults['bom'] = self.start.bom.id
         elif product.boms:
             defaults['bom'] = product.boms[0].id
-        defaults['quantity'] = session.start.quantity
+        defaults['quantity'] = getattr(self.start, 'quantity', None)
         return defaults
 
-    def default_tree(self, session, fields):
+    def default_tree(self, fields):
         pool = Pool()
-        bom_tree_obj = pool.get('production.bom.tree.open.tree')
-        return bom_tree_obj.tree(session.start.bom.bom, session.start.product,
-            session.start.quantity, session.start.uom)
-
-OpenBOMTree()
+        BomTree = pool.get('production.bom.tree.open.tree')
+        return BomTree.tree(self.start.bom.bom, self.start.product,
+            self.start.quantity, self.start.uom)
diff -r 98a16ccdf418 trytond/modules/production/configuration.py
--- a/trytond/modules/production/configuration.py	Mon Sep 10 15:28:23 2012 +0200
+++ b/trytond/modules/production/configuration.py	Mon Sep 24 20:36:41 2012 +0200
@@ -3,11 +3,12 @@
 from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
 from trytond.pyson import Eval
 
+__all__ = ['Configuration']
+
 
 class Configuration(ModelSingleton, ModelSQL, ModelView):
     'Production Configuration'
-    _name = 'production.configuration'
-    _description = __doc__
+    __name__ = 'production.configuration'
 
     production_sequence = fields.Property(fields.Many2One('ir.sequence',
             'Production Sequence', domain=[
@@ -16,4 +17,3 @@
                 ('code', '=', 'production'),
                 ], required=True))
 
-Configuration()
diff -r 98a16ccdf418 trytond/modules/production/product.py
--- a/trytond/modules/production/product.py	Mon Sep 10 15:28:23 2012 +0200
+++ b/trytond/modules/production/product.py	Mon Sep 24 20:36:41 2012 +0200
@@ -2,10 +2,14 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.pyson import Eval, Get, If, Bool
+from trytond.pool import PoolMeta
 
+__all__ = ['Product', 'ProductBom']
+__metaclass__ = PoolMeta
 
-class Product(ModelSQL, ModelView):
-    _name = 'product.product'
+
+class Product:
+    __name__ = 'product.product'
 
     boms = fields.One2Many('product.product-production.bom', 'product',
         'BOMs', order=[('sequence', 'ASC'), ('id', 'ASC')],
@@ -14,39 +18,34 @@
             },
         depends=['type'])
 
-    def __init__(self):
-        super(Product, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Product, cls).__setup__()
+        cls._constraints += [
             ('check_bom_recursion', 'recursive_bom'),
-        ]
-        self._error_messages.update({
-            'recursive_bom': 'You can not create recursive BOMs!',
-        })
+            ]
+        cls._error_messages.update({
+                'recursive_bom': 'You can not create recursive BOMs!',
+                })
 
-    def check_bom_recursion(self, ids):
+    def check_bom_recursion(self, product=None):
         '''
         Check BOM recursion
         '''
-        def check(sub_product, product):
-            for product_bom in sub_product.boms:
-                for input in product_bom.bom.inputs:
-                    if input.product == product:
-                        return False
-                    if not check(input.product, product):
-                        return False
-            return True
-        for product in self.browse(ids):
-            if not check(product, product):
-                return False
+        if product is None:
+            product = self
+        for product_bom in self.boms:
+            for input_ in product_bom.bom.inputs:
+                if input_.product == product:
+                    return False
+                if not input_.product.check_bom_recursion(product=product):
+                    return False
         return True
 
-Product()
-
 
 class ProductBom(ModelSQL, ModelView):
     'Product - BOM'
-    _name = 'product.product-production.bom'
-    _description = __doc__
+    __name__ = 'product.product-production.bom'
 
     product = fields.Many2One('product.product', 'Product',
         ondelete='CASCADE', select=1, required=True,
@@ -61,17 +60,14 @@
             ], depends=['product'])
     sequence = fields.Integer('Sequence')
 
-    def __init__(self):
-        super(ProductBom, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ProductBom, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def get_rec_name(self, ids, name):
-        result = {}
-        for product_bom in self.browse(ids):
-            result[product_bom.id] = product_bom.bom.rec_name
-        return result
+    def get_rec_name(self, name):
+        return self.bom.rec_name
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         return [('bom.rec_name',) + tuple(clause[1:])]
-
-ProductBom()
diff -r 98a16ccdf418 trytond/modules/production/production.py
--- a/trytond/modules/production/production.py	Mon Sep 10 15:28:23 2012 +0200
+++ b/trytond/modules/production/production.py	Mon Sep 24 20:36:41 2012 +0200
@@ -8,14 +8,15 @@
 from trytond.pool import Pool
 from trytond.transaction import Transaction
 
+__all__ = ['Production', 'AssignFailed', 'Assign']
+
 BOM_CHANGES = ['bom', 'product', 'quantity', 'uom', 'warehouse', 'location',
     'company', 'inputs', 'outputs']
 
 
 class Production(Workflow, ModelSQL, ModelView):
     "Production"
-    _name = 'production'
-    _description = __doc__
+    __name__ = 'production'
     _rec_name = 'code'
 
     code = fields.Char('Code', select=True, readonly=True)
@@ -75,7 +76,7 @@
         depends=['product'])
     uom_category = fields.Function(fields.Many2One(
             'product.uom.category', 'Uom Category',
-            on_change_with=['product']), 'get_uom_category')
+            on_change_with=['product']), 'on_change_with_uom_category')
     uom = fields.Many2One('product.uom', 'Uom',
         domain=[
             ('category', '=', Eval('uom_category')),
@@ -88,7 +89,7 @@
         on_change=BOM_CHANGES,
         depends=['uom_category'])
     unit_digits = fields.Function(fields.Integer('Unit Digits',
-            on_change_with=['uom']), 'get_unit_digits')
+            on_change_with=['uom']), 'on_change_with_unit_digits')
     quantity = fields.Float('Quantity',
         digits=(16, Eval('unit_digits', 2)),
         states={
@@ -130,15 +131,16 @@
             ('cancel', 'Canceled'),
             ], 'State', readonly=True)
 
-    def __init__(self):
-        super(Production, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Production, cls).__setup__()
+        cls._constraints += [
             ('check_cost', 'missing_cost'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
                 'missing_cost': 'It misses some cost on the outputs!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('request', 'draft'),
                 ('draft', 'waiting'),
                 ('waiting', 'assigned'),
@@ -154,7 +156,7 @@
                 ('assigned', 'cancel'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': ~Eval('state').in_(['request', 'draft',
                             'assigned']),
@@ -192,88 +194,79 @@
                 'assign_force': {},
                 })
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_warehouse(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(self.warehouse.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_warehouse(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.warehouse.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def default_location(self):
-        location_obj = Pool().get('stock.location')
-        warehouse_id = self.default_warehouse()
+    @classmethod
+    def default_location(cls):
+        Location = Pool().get('stock.location')
+        warehouse_id = cls.default_warehouse()
         if warehouse_id:
-            warehouse = location_obj.browse(warehouse_id)
+            warehouse = Location(warehouse_id)
             return warehouse.production_location.id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def _move_values(self, from_location, to_location, company, product, uom,
+    def _move(self, from_location, to_location, company, product, uom,
             quantity):
-        values = {
-            'product': product.id,
-            'uom': uom.id,
-            'quantity': quantity,
-            'from_location': None,
-            'to_location': None,
-            'company': None,
-            'state': 'draft',
-            }
-        values['currency'] = company.currency.id if company else None
-        if from_location:
-            values['from_location'] = from_location.id
-        if to_location:
-            values['to_location'] = to_location.id
-        if company:
-            values['company'] = company.id
-        return values
+        Move = Pool().get('stock.move')
+        move = Move(
+            product=product,
+            uom=uom,
+            quantity=quantity,
+            from_location=from_location,
+            to_location=to_location,
+            company=company,
+            currency=company.currency if company else None,
+            state='draft',
+            )
+        return move
 
     def _explode_move_values(self, from_location, to_location, company,
             bom_io, quantity):
         pool = Pool()
-        move_obj = pool.get('stock.move')
+        Move = pool.get('stock.move')
 
-        values = self._move_values(from_location, to_location, company,
+        move = self._move(from_location, to_location, company,
             bom_io.product, bom_io.uom, quantity)
-        values['product.rec_name'] = bom_io.product.rec_name
-        values['uom.rec_name'] = bom_io.uom.rec_name
-        values['unit_price_required'] = \
-            move_obj.on_change_with_unit_price_required({
-                    'from_location': (from_location.id if from_location else
-                        None),
-                    'to_location': to_location.id if to_location else None,
-                    })
-        if from_location:
-            values['from_location.rec_name'] = from_location.rec_name
-        if to_location:
-            values['to_location.rec_name'] = to_location.rec_name
-        if company:
-            values['company.rec_name'] = company.rec_name
+        move.from_location = from_location.id if from_location else None
+        move.to_location = to_location.id if to_location else None
+        move.unit_price_required = move.on_change_with_unit_price_required()
+        values = {}
+        for field_name, field in Move._fields.iteritems():
+            try:
+                value = getattr(move, field_name)
+            except AttributeError:
+                continue
+            if value and field._type in ('many2one', 'one2one'):
+                values[field_name] = value.id
+                values[field_name + '.rec_name'] = value.rec_name
+            else:
+                values[field_name] = value
         return values
 
-    def explode_bom(self, values):
+    def explode_bom(self):
         pool = Pool()
-        bom_obj = pool.get('production.bom')
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
-        input_obj = pool.get('production.bom.input')
-        output_obj = pool.get('production.bom.output')
-        location_obj = pool.get('stock.location')
-        company_obj = pool.get('company.company')
+        Uom = pool.get('product.uom')
 
-        if not (values.get('bom')
-                and values.get('product')
-                and values.get('uom')):
+        if not (self.bom and self.product and self.uom):
             return {}
         inputs = {
-            'remove': [r['id'] for r in values.get('inputs') or []],
+            'remove': [r.id for r in self.inputs or []],
             'add': [],
             }
         outputs = {
-            'remove': [r['id'] for r in values.get('outputs') or []],
+            'remove': [r.id for r in self.outputs or []],
             'add': [],
             }
         changes = {
@@ -282,40 +275,28 @@
             'cost': Decimal(0),
             }
 
-        bom = bom_obj.browse(values['bom'])
-        product = product_obj.browse(values['product'])
-        quantity = values.get('quantity') or 0
-        uom = uom_obj.browse(values['uom'])
-        if values.get('warehouse'):
-            warehouse = location_obj.browse(values['warehouse'])
-            storage_location = warehouse.storage_location
+        if self.warehouse:
+            storage_location = self.warehouse.storage_location
         else:
             storage_location = None
-        if values.get('location'):
-            location = location_obj.browse(values['location'])
-        else:
-            location = None
-        if values.get('company'):
-            company = company_obj.browse(values['company'])
-        else:
-            company = None
 
-        factor = bom_obj.compute_factor(bom, product, quantity, uom)
-        for input_ in bom.inputs:
-            quantity = input_obj.compute_quantity(input_, factor)
-            values = self._explode_move_values(storage_location, location,
-                company, input_, quantity)
+        factor = self.bom.compute_factor(self.product, self.quantity or 0,
+            self.uom)
+        for input_ in self.bom.inputs:
+            quantity = input_.compute_quantity(factor)
+            values = self._explode_move_values(storage_location, self.location,
+                self.company, input_, quantity)
             if values:
                 inputs['add'].append(values)
-                quantity = uom_obj.compute_qty(input_.uom, quantity,
+                quantity = Uom.compute_qty(input_.uom, quantity,
                     input_.product.default_uom)
                 changes['cost'] += (Decimal(str(quantity)) *
                     input_.product.cost_price)
 
-        for output in bom.outputs:
-            quantity = output_obj.compute_quantity(output, factor)
-            values = self._explode_move_values(location, storage_location,
-                company, output, quantity)
+        for output in self.bom.outputs:
+            quantity = output.compute_quantity(factor)
+            values = self._explode_move_values(self.location, storage_location,
+                self.company, output, quantity)
             if values:
                 values['unit_price'] = Decimal(0)
                 if output.product.id == values.get('product') and quantity:
@@ -324,341 +305,284 @@
                 outputs['add'].append(values)
         return changes
 
-    def on_change_warehouse(self, values):
-        location_obj = Pool().get('stock.location')
+    def on_change_warehouse(self):
         changes = {
             'location': None,
             }
-        if values.get('warehouse'):
-            warehouse = location_obj.browse(values['warehouse'])
-            changes['location'] = warehouse.production_location.id
+        if self.warehouse:
+            changes['location'] = self.warehouse.production_location.id
         return changes
 
-    def on_change_product(self, values):
-        product_obj = Pool().get('product.product')
-
+    def on_change_product(self):
         result = {}
-        if values.get('product'):
-            product = product_obj.browse(values['product'])
-            uom_ids = [x.id for x in product.default_uom.category.uoms]
-            if (not values.get('uom')
-                    or values.get('uom') not in uom_ids):
-                result['uom'] = product.default_uom.id
-                result['uom.rec_name'] = product.default_uom.rec_name
-                result['unit_digits'] = product.default_uom.digits
+        if self.product:
+            uoms = self.product.default_uom.category.uoms
+            if (not self.uom or self.uom not in uoms):
+                result['uom'] = self.product.default_uom.id
+                result['uom.rec_name'] = self.product.default_uom.rec_name
+                result['unit_digits'] = self.product.default_uom.digits
         else:
             result['bom'] = None
             result['uom'] = None
             result['uom.rec_name'] = ''
             result['unit_digits'] = 2
 
-        values = values.copy()
-        values['uom'] = result['uom']
+        self.uom = result['uom']
         if 'bom' in result:
-            values['bom'] = result['bom']
-        result.update(self.explode_bom(values))
+            self.bom = result['bom']
+        result.update(self.explode_bom())
         return result
 
-    def on_change_with_uom_category(self, values):
-        product_obj = Pool().get('product.product')
-        if values.get('product'):
-            product = product_obj.browse(values['product'])
-            return product.default_uom.category.id
+    def on_change_with_uom_category(self, name=None):
+        if self.product:
+            return self.product.default_uom.category.id
 
-    def get_uom_category(self, ids, name):
-        res = {}
-        for production in self.browse(ids):
-            if production.product:
-                res[production.id] = production.product.default_uom.category.id
-            else:
-                res[production.id] = None
-        return res
-
-    def on_change_with_unit_digits(self, values):
-        uom_obj = Pool().get('product.uom')
-        if values.get('uom'):
-            uom = uom_obj.browse(values['uom'])
-            return uom.digits
+    def on_change_with_unit_digits(self, name=None):
+        if self.uom:
+            return self.uom.digits
         return 2
 
-    def get_unit_digits(self, ids, name):
-        digits = {}
-        for production in self.browse(ids):
-            if production.uom:
-                digits[production.id] = production.uom.digits
+    def on_change_bom(self):
+        return self.explode_bom()
+
+    def on_change_uom(self):
+        return self.explode_bom()
+
+    def on_change_quantity(self):
+        return self.explode_bom()
+
+    def get_cost(self, name):
+        cost = Decimal(0)
+        for input_ in self.inputs:
+            if input_.cost_price is not None:
+                cost_price = input_.cost_price
             else:
-                digits[production.id] = 2
-        return digits
+                cost_price = input_.product.cost_price
+            cost += (Decimal(str(input_.internal_quantity)) * cost_price)
+        return cost
 
-    def on_change_bom(self, values):
-        return self.explode_bom(values)
-
-    def on_change_uom(self, values):
-        return self.explode_bom(values)
-
-    def on_change_quantity(self, values):
-        return self.explode_bom(values)
-
-    def get_cost(self, ids, name):
-        costs = {}
-        for production in self.browse(ids):
-            costs[production.id] = Decimal(0)
-            for input_ in production.inputs:
-                if input_.cost_price is not None:
-                    cost_price = input_.cost_price
-                else:
-                    cost_price = input_.product.cost_price
-                costs[production.id] += (Decimal(str(input_.internal_quantity))
-                        * cost_price)
-        return costs
-
-    def on_change_with_cost(self, values):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
+    def on_change_with_cost(self):
+        Uom = Pool().get('product.uom')
 
         cost = Decimal(0)
-        if not values.get('inputs'):
+        if not self.inputs:
             return cost
 
-        product_ids = list(set(r['product'] for r in values['inputs'] if
-                r['product'] is not None))
-        id2product = dict((p.id, p) for p in product_obj.browse(product_ids))
-
-        uom_ids = list(set(r['uom'] for r in values['inputs']))
-        id2uom = dict((u.id, u) for u in uom_obj.browse(uom_ids))
-
-        for input_ in values['inputs']:
-            if (input_['product'] is None
-                    or input_['uom'] is None
-                    or input_['quantity'] is None):
+        for input_ in self.inputs:
+            if (input_.product is None
+                    or input_.uom is None
+                    or input_.quantity is None):
                 continue
-            product = id2product[input_['product']]
-            quantity = uom_obj.compute_qty(id2uom[input_['uom']],
-                input_['quantity'], product.default_uom)
+            product = input_.product
+            quantity = Uom.compute_qty(input_.uom, input_.quantity,
+                product.default_uom)
             cost += Decimal(str(quantity)) * product.cost_price
         return cost
 
-    def set_moves(self, production):
-        pool = Pool()
-        bom_obj = pool.get('production.bom')
-        move_obj = pool.get('stock.move')
-        input_obj = pool.get('production.bom.input')
-        output_obj = pool.get('production.bom.output')
+    def set_moves(self):
+        storage_location = self.warehouse.storage_location
+        location = self.location
+        company = self.company
 
-        storage_location = production.warehouse.storage_location
-        location = production.location
-        company = production.company
-
-        if not production.bom:
-            if production.product:
-                product = production.product
-                values = self._move_values(location, storage_location, company,
-                    product, product.default_uom)
-                if values:
-                    values['production_output'] = production.id
-                    move_obj.create(values)
+        if not self.bom:
+            if self.product:
+                move = self._move(location, storage_location, company,
+                    self.product, self.product.default_uom)
+                if move:
+                    move.production_output = self
+                    move.save()
             return
 
-        factor = bom_obj.compute_factor(production.bom, production.product,
-            production.quantity, production.uom)
+        factor = self.bom.compute_factor(self.product, self.quantity, self.uom)
         cost = Decimal(0)
-        for input_ in production.bom.inputs:
-            quantity = input_obj.compute_quantity(input_, factor)
+        for input_ in self.bom.inputs:
+            quantity = input_.compute_quantity(factor)
             product = input_.product
-            values = self._move_values(storage_location, location, company,
+            move = self._move(storage_location, location, company,
                 product, input_.uom, quantity)
-            if values:
-                values['production_input'] = production.id
-                move_obj.create(values)
+            if move:
+                move.production_input = self
+                move.save()
                 cost += Decimal(str(quantity)) * product.cost_price
 
-        for output in production.bom.outputs:
-            quantity = output_obj.compute_quantity(output, factor)
+        for output in self.bom.outputs:
+            quantity = output.compute_quantity(factor)
             product = output.product
-            values = self._move_values(location, storage_location, company,
+            move = self._move(location, storage_location, company,
                 product, output.uom, quantity)
-            if values:
-                values['production_output'] = production.id
-                if product == production.product:
-                    values['unit_price'] = cost / Decimal(str(quantity))
-                move_obj.create(values)
-        self._set_move_planned_date([production.id])
+            if move:
+                move.production_output = self
+                if product == self.product:
+                    move.unit_price = cost / Decimal(str(quantity))
+                move.save()
+        self._set_move_planned_date()
 
-    def check_cost(self, ids):
-        pool = Pool()
-        currency_obj = pool.get('currency.currency')
-
-        for production in self.browse(ids):
-            if production.state != 'done':
-                continue
-            cost_price = Decimal(0)
-            for output in production.outputs:
-                cost_price += (Decimal(str(output.quantity))
-                    * output.unit_price)
-            if not currency_obj.is_zero(production.company.currency,
-                    production.cost - cost_price):
-                return False
+    def check_cost(self):
+        if self.state != 'done':
+            return True
+        cost_price = Decimal(0)
+        for output in self.outputs:
+            cost_price += (Decimal(str(output.quantity))
+                * output.unit_price)
+        if not self.company.currency.is_zero(self.cost - cost_price):
+            return False
         return True
 
-    def create(self, values):
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('production.configuration')
+    @classmethod
+    def create(cls, values):
+        Sequence = Pool().get('ir.sequence')
+        Config = Pool().get('production.configuration')
 
         values = values.copy()
-        config = config_obj.browse(1)
-        values['code'] = sequence_obj.get_id(config.production_sequence.id)
-        production_id = super(Production, self).create(values)
-        self._set_move_planned_date(production_id)
-        return production_id
+        config = Config(1)
+        values['code'] = Sequence.get_id(config.production_sequence.id)
+        production = super(Production, cls).create(values)
+        production._set_move_planned_date()
+        return production
 
-    def write(self, ids, values):
-        result = super(Production, self).write(ids, values)
-        self._set_move_planned_date(ids)
-        return result
+    @classmethod
+    def write(cls, productions, values):
+        super(Production, cls).write(productions, values)
+        for production in productions:
+            production._set_move_planned_date()
 
-    def _get_move_planned_date(self, production):
+    def _get_move_planned_date(self):
         "Return the planned dates for input and output moves"
-        return production.planned_date, production.planned_date
+        return self.planned_date, self.planned_date
 
-    def _set_move_planned_date(self, ids):
+    def _set_move_planned_date(self):
         "Set planned date of moves for the shipments"
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for production in self.browse(ids):
-            dates = self._get_move_planned_date(production)
-            input_date, output_date = dates
-            move_obj.write([m.id for m in production.inputs
-                    if m.state not in ('assigned', 'done', 'cancel')], {
-                    'planned_date': input_date,
-                    })
-            move_obj.write([m.id for m in production.outputs
-                    if m.state not in ('assigned', 'done', 'cancel')], {
-                    'planned_date': output_date,
-                    })
+        Move = pool.get('stock.move')
+        dates = self._get_move_planned_date()
+        input_date, output_date = dates
+        Move.write([m for m in self.inputs
+                if m.state not in ('assigned', 'done', 'cancel')], {
+                'planned_date': input_date,
+                })
+        Move.write([m for m in self.outputs
+                if m.state not in ('assigned', 'done', 'cancel')], {
+                'planned_date': output_date,
+                })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
+    def cancel(cls, productions):
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        productions = self.browse(ids)
-        move_obj.write([m.id for p in productions
+        Move = pool.get('stock.move')
+        Move.write([m for p in productions
                 for m in p.inputs + p.outputs
                 if m.state != 'cancel'], {
                 'state': 'cancel',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
+    def draft(cls, productions):
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        productions = self.browse(ids)
-        move_obj.write([m.id for p in productions
+        Move = pool.get('stock.move')
+        Move.write([m for p in productions
                 for m in p.inputs + p.outputs
                 if m.state != 'draft'], {
                 'state': 'draft',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('waiting')
-    def wait(self, ids):
+    def wait(cls, productions):
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        productions = self.browse(ids)
-        move_obj.write([m.id for p in productions
+        Move = pool.get('stock.move')
+        Move.write([m for p in productions
                 for m in p.inputs + p.outputs
                 if m.state not in ('draft', 'done')], {
                 'state': 'draft',
                 })
 
+    @classmethod
     @Workflow.transition('assigned')
-    def assign(self, ids):
+    def assign(cls, productions):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('running')
-    def run(self, ids):
+    def run(cls, productions):
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        productions = self.browse(ids)
-        move_obj.write([m.id for p in productions
+        Move = pool.get('stock.move')
+        Move.write([m for p in productions
                 for m in p.inputs
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('done')
-    def done(self, ids):
+    def done(cls, productions):
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        date_obj = pool.get('ir.date')
-        productions = self.browse(ids)
-        move_obj.write([m.id for p in productions
+        Move = pool.get('stock.move')
+        Date = pool.get('ir.date')
+        Move.write([m for p in productions
                 for m in p.outputs
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
-        self.write(ids, {
-                'effective_date': date_obj.today(),
+        cls.write(productions, {
+                'effective_date': Date.today(),
                 })
 
+    @classmethod
     @ModelView.button_action('production.wizard_assign')
-    def assign_wizard(self, ids):
+    def assign_wizard(self, productions):
         pass
 
+    @classmethod
     @ModelView.button
-    def assign_try(self, ids):
+    def assign_try(cls, productions):
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        productions = self.browse(ids)
-        if move_obj.assign_try([m for p in productions
+        Move = pool.get('stock.move')
+        if Move.assign_try([m for p in productions
                     for m in p.inputs]):
-            self.assign(ids)
+            cls.assign(productions)
             return True
         else:
             return False
 
+    @classmethod
     @ModelView.button
-    def assign_force(self, ids):
+    def assign_force(cls, productions):
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        productions = self.browse(ids)
-        move_obj.write([m.id for p in productions for m in p.inputs
+        Move = pool.get('stock.move')
+        Move.write([m for p in productions for m in p.inputs
                 if m.state != 'done'], {
                 'state': 'assigned',
                 })
-        self.assign(ids)
-
-Production()
+        cls.assign(productions)
 
 
 class AssignFailed(ModelView):
     'Assign Production'
-    _name = 'production.assign.failed'
-    _description = __doc__
+    __name__ = 'production.assign.failed'
 
     moves = fields.Many2Many('stock.move', None, None, 'Moves', readonly=True)
 
-    def default_moves(self):
+    @staticmethod
+    def default_moves():
         pool = Pool()
-        production_obj = pool.get('production')
+        Production = pool.get('production')
         production_id = Transaction().context.get('active_id')
         if not production_id:
             return []
-        production = production_obj.browse(production_id)
+        production = Production(production_id)
         return [m.id for m in production.inputs if m.state == 'draft']
 
-AssignFailed()
-
 
 class Assign(Wizard):
     'Assign Production'
-    _name = 'production.assign'
+    __name__ = 'production.assign'
 
     start = StateTransition()
     failed = StateView('production.assign.failed',
@@ -673,20 +597,20 @@
             ])
     force = StateTransition()
 
-    def transition_start(self, session):
+    def transition_start(self):
         pool = Pool()
-        production_obj = pool.get('production')
+        Production = pool.get('production')
 
-        if production_obj.assign_try([Transaction().context['active_id']]):
+        if Production.assign_try(
+                [Production(Transaction().context['active_id'])]):
             return 'end'
         else:
             return 'failed'
 
-    def transition_force(self, session):
+    def transition_force(self):
         pool = Pool()
-        production_obj = pool.get('production')
+        Production = pool.get('production')
 
-        production_obj.assign_force([Transaction().context['active_id']])
+        Production.assign_force(
+            [Production(Transaction().context['active_id'])])
         return 'end'
-
-Assign()
diff -r 98a16ccdf418 trytond/modules/production/stock.py
--- a/trytond/modules/production/stock.py	Mon Sep 10 15:28:23 2012 +0200
+++ b/trytond/modules/production/stock.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,12 +1,16 @@
 #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 Model, fields
+from trytond.model import fields
 from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 
 
-class Location(Model):
-    _name = 'stock.location'
+__all__ = ['Location', 'Move']
+__metaclass__ = PoolMeta
 
+
+class Location:
+    __name__ = 'stock.location'
     production_location = fields.Many2One('stock.location', 'Production',
         states={
             'invisible': Eval('type') != 'warehouse',
@@ -18,12 +22,9 @@
             ],
         depends=['type', 'active'])
 
-Location()
 
-
-class Move(Model):
-    _name = 'stock.move'
-
+class Move:
+    __name__ = 'stock.move'
     production_input = fields.Many2One('production', 'Production Input',
         readonly=True, select=True, ondelete='CASCADE',
         domain=[('company', '=', Eval('company'))],
@@ -32,5 +33,3 @@
         readonly=True, select=True, ondelete='CASCADE',
         domain=[('company', '=', Eval('company'))],
         depends=['company'])
-
-Move()
diff -r 10e5ca37fbe7 trytond/modules/project/__init__.py
--- a/trytond/modules/project/__init__.py	Fri Sep 21 16:01:35 2012 +0200
+++ b/trytond/modules/project/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,4 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .work import *
+
+
+def register():
+    Pool.register(
+        Work,
+        module='project', type_='model')
diff -r 10e5ca37fbe7 trytond/modules/project/work.py
--- a/trytond/modules/project/work.py	Fri Sep 21 16:01:35 2012 +0200
+++ b/trytond/modules/project/work.py	Mon Sep 24 20:36:41 2012 +0200
@@ -6,13 +6,13 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Work']
+
 
 class Work(ModelSQL, ModelView):
     'Work Effort'
-    _name = 'project.work'
-    _description = __doc__
+    __name__ = 'project.work'
     _inherits = {'timesheet.work': 'work'}
-
     work = fields.Many2One('timesheet.work', 'Work', required=True,
             ondelete='CASCADE')
     type = fields.Selection([
@@ -48,25 +48,28 @@
         order_field='(%(table)s.sequence IS NULL) %(order)s, '
         '%(table)s.sequence %(order)s')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'task'
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'opened'
 
-    def default_effort(self):
+    @staticmethod
+    def default_effort():
         return 0.0
 
-    def init(self, module_name):
-        timesheet_work_obj = Pool().get('timesheet.work')
+    @classmethod
+    def __register__(cls, module_name):
+        TimesheetWork = Pool().get('timesheet.work')
         cursor = Transaction().cursor
-        table_project_work = TableHandler(cursor, self, module_name)
-        table_timesheet_work = TableHandler(cursor, timesheet_work_obj,
-            module_name)
+        table_project_work = TableHandler(cursor, cls, module_name)
+        table_timesheet_work = TableHandler(cursor, TimesheetWork, module_name)
         migrate_sequence = (not table_project_work.column_exist('sequence')
             and table_timesheet_work.column_exist('sequence'))
 
-        super(Work, self).init(module_name)
+        super(Work, cls).__register__(module_name)
 
         # Migration from 2.0: copy sequence from timesheet to project
         if migrate_sequence:
@@ -74,79 +77,81 @@
                 'SELECT t.sequence, t.id '
                 'FROM "%s" AS t '
                 'JOIN "%s" AS p ON (p.work = t.id)' % (
-                    timesheet_work_obj._table, self._table))
+                    TimesheetWork._table, cls._table))
             for sequence, id_ in cursor.fetchall():
                 sql = ('UPDATE "%s" '
                         'SET sequence = %%s '
-                        'WHERE work = %%s' % self._table)
+                        'WHERE work = %%s' % cls._table)
                 cursor.execute(sql, (sequence, id_))
 
         # Migration from 2.4: drop required on sequence
         table_project_work.not_null_action('sequence', action='remove')
 
-    def __init__(self):
-        super(Work, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Work, cls).__setup__()
+        cls._sql_constraints += [
             ('work_uniq', 'UNIQUE(work)', 'There should be only one '\
                  'timesheet work by task/project!'),
-        ]
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._constraints += [
+            ]
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._constraints += [
             ('check_state',
                 'A work can not be closed if its children are still opened'),
             ]
 
-    def check_state(self, ids):
-        for work in self.browse(ids):
-            if ((work.state == 'opened'
-                        and (work.parent and work.parent.state == 'done'))
-                    or (work.state == 'done'
-                        and any(c.state == 'opened' for c in work.children))):
-                return False
+    def check_state(self):
+        if ((self.state == 'opened'
+                    and (self.parent and self.parent.state == 'done'))
+                or (self.state == 'done'
+                    and any(c.state == 'opened' for c in self.children))):
+            return False
         return True
 
-    def get_parent(self, ids, name):
-        res = dict.fromkeys(ids, None)
-        project_works = self.browse(ids)
+    @classmethod
+    def get_parent(cls, project_works, name):
+        parents = dict.fromkeys([w.id for w in project_works], None)
 
         # ptw2pw is "parent timesheet work to project works":
         ptw2pw = {}
         for project_work in project_works:
+            if not project_work.work.parent:
+                continue
             if project_work.work.parent.id in ptw2pw:
                 ptw2pw[project_work.work.parent.id].append(project_work.id)
             else:
                 ptw2pw[project_work.work.parent.id] = [project_work.id]
 
         with Transaction().set_context(active_test=False):
-            parent_project_ids = self.search([
+            parent_projects = cls.search([
                     ('work', 'in', ptw2pw.keys()),
                     ])
-        parent_projects = self.browse(parent_project_ids)
         for parent_project in parent_projects:
             if parent_project.work.id in ptw2pw:
                 child_projects = ptw2pw[parent_project.work.id]
                 for child_project in child_projects:
-                    res[child_project] = parent_project.id
+                    parents[child_project] = parent_project.id
 
-        return res
+        return parents
 
-    def set_parent(self, ids, name, value):
-        timesheet_work_obj = Pool().get('timesheet.work')
+    @classmethod
+    def set_parent(cls, project_works, name, value):
+        TimesheetWork = Pool().get('timesheet.work')
         if value:
-            project_works = self.browse(ids + [value])
-            child_timesheet_work_ids = [x.work.id for x in project_works[:-1]]
+            project_works.append(cls(value))
+            child_timesheet_works = [x.work for x in project_works[:-1]]
             parent_timesheet_work_id = project_works[-1].work.id
         else:
-            child_project_works = self.browse(ids)
-            child_timesheet_work_ids = [x.work.id for x in child_project_works]
+            child_timesheet_works = [x.work for x in project_works]
             parent_timesheet_work_id = None
 
-        timesheet_work_obj.write(child_timesheet_work_ids, {
+        TimesheetWork.write(child_timesheet_works, {
                 'parent': parent_timesheet_work_id
                 })
 
-    def search_parent(self, name, domain=None):
-        timesheet_work_obj = Pool().get('timesheet.work')
+    @classmethod
+    def search_parent(cls, name, domain):
+        TimesheetWork = Pool().get('timesheet.work')
 
         project_work_domain = []
         timesheet_work_domain = []
@@ -172,11 +177,11 @@
         if operands:
             operands = list(operands)
             # filter out non-existing ids:
-            operands = self.search([
+            operands = cls.search([
                     ('id', 'in', operands)
                     ])
             # create project_work > timesheet_work mapping
-            for pw in self.browse(operands):
+            for pw in operands:
                 pw2tw[pw.id] = pw.work.id
 
             for i, d in enumerate(timesheet_work_domain):
@@ -190,24 +195,22 @@
                 timesheet_work_domain[i] = (d[0], d[1], new_d2)
 
         if project_work_domain:
-            pw_ids = self.search(project_work_domain)
-            project_works = self.browse(pw_ids)
+            project_works = cls.search(project_work_domain)
             timesheet_work_domain.append(
                 ('id', 'in', [pw.work.id for pw in project_works]))
 
-        tw_ids = timesheet_work_obj.search(timesheet_work_domain)
+        tw_ids = [tw.id for tw in TimesheetWork.search(timesheet_work_domain)]
 
         return [('work', 'in', tw_ids)]
 
-    def get_total_effort(self, ids, name):
+    @classmethod
+    def get_total_effort(cls, works, name):
 
-        all_ids = self.search([
-                ('parent', 'child_of', ids),
+        works += cls.search([
+                ('parent', 'child_of', [w.id for w in works]),
                 ('active', '=', True),
-                ]) + ids
-        all_ids = list(set(all_ids))
-
-        works = self.browse(all_ids)
+                ])
+        works = list(set(works))
 
         res = {}
         id2work = {}
@@ -231,46 +234,34 @@
 
         return res
 
-    def copy(self, ids, default=None):
-        timesheet_work_obj = Pool().get('timesheet.work')
-
-        int_id = isinstance(ids, (int, long))
-        if int_id:
-            ids = [ids]
+    @classmethod
+    def copy(cls, project_works, default=None):
+        TimesheetWork = Pool().get('timesheet.work')
 
         if default is None:
             default = {}
 
         timesheet_default = default.copy()
         for key in timesheet_default.keys():
-            if key in self._columns:
+            if key in cls._fields:
                 del timesheet_default[key]
-        new_ids = []
-        for project_work in self.browse(ids):
-            timesheet_work_id = timesheet_work_obj.copy(project_work.work.id,
+        new_project_works = []
+        for project_work in project_works:
+            timesheet_work, = TimesheetWork.copy([project_work.work],
                 default=timesheet_default)
             pwdefault = default.copy()
-            pwdefault['work'] = timesheet_work_id
-            new_ids.append(super(Work, self).copy(project_work.id,
-                default=pwdefault))
-        if int_id:
-            return new_ids[0]
-        return new_ids
+            pwdefault['work'] = timesheet_work.id
+            new_project_works.extend(super(Work, cls).copy([project_work],
+                    default=pwdefault))
+        return new_project_works
 
-    def delete(self, ids):
-        timesheet_work_obj = Pool().get('timesheet.work')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, project_works):
+        TimesheetWork = Pool().get('timesheet.work')
 
         # Get the timesheet works linked to the project works
-        project_works = self.browse(ids)
-        timesheet_work_ids = [pw.work.id for pw in project_works]
+        timesheet_works = [pw.work for pw in project_works]
 
-        res = super(Work, self).delete(ids)
+        super(Work, cls).delete(project_works)
 
-        timesheet_work_obj.delete(timesheet_work_ids)
-        return res
-
-
-Work()
+        TimesheetWork.delete(timesheet_works)
diff -r d86297b89cb9 trytond/modules/project_plan/__init__.py
--- a/trytond/modules/project_plan/__init__.py	Thu Jul 26 08:06:23 2012 +0530
+++ b/trytond/modules/project_plan/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,5 +1,17 @@
 #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.pool import Pool
 from .allocation import *
 from .work import *
+
+
+def register():
+    Pool.register(
+        Allocation,
+        Work,
+        PredecessorSuccessor,
+        module='project_plan', type_='model')
+    Pool.register(
+        Leveling,
+        module='project_plan', type_='wizard')
diff -r d86297b89cb9 trytond/modules/project_plan/allocation.py
--- a/trytond/modules/project_plan/allocation.py	Thu Jul 26 08:06:23 2012 +0530
+++ b/trytond/modules/project_plan/allocation.py	Mon Sep 24 20:36:41 2012 +0200
@@ -3,64 +3,57 @@
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.pool import Pool
 
+__all__ = ['Allocation']
+
 
 class Allocation(ModelSQL, ModelView):
     'Allocation'
-    _name = 'project.allocation'
-    _description = __doc__
+    __name__ = 'project.allocation'
     _rec_name = 'employee'
-
     employee = fields.Many2One('company.employee', 'Employee', required=True,
             select=True)
     work = fields.Many2One('project.work', 'Work', required=True,
             select=True)
     percentage = fields.Float('Percentage', digits=(16, 2), required=True)
 
-    def __init__(self):
-        super(Allocation, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Allocation, cls).__setup__()
+        cls._sql_constraints += [
             ('percentage_positive', 'CHECK(percentage > 0)',
                 'Percentage must be greater than zero')
-        ]
+            ]
 
-    def default_percentage(self):
+    @staticmethod
+    def default_percentage():
         return 100
 
-    def write(self, ids, values):
-        work_obj = Pool().get('project.work')
-        res = super(Allocation, self).write(ids, values)
+    @classmethod
+    def write(cls, allocations, values):
+        Work = Pool().get('project.work')
+        super(Allocation, cls).write(allocations, values)
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        work_ids = work_obj.search([
-                ('allocations', 'in', ids),
+        works = Work.search([
+                ('allocations', 'in', [a.id for a in allocations]),
                 ])
 
-        for work_id in work_ids:
-            work_obj.reset_leveling(work_id)
-        for work_id in work_ids:
-            work_obj.compute_dates(work_id)
+        for work in works:
+            work.reset_leveling()
+        for work in works:
+            work.compute_dates()
 
-        return res
+    @classmethod
+    def create(cls, values):
+        allocation = super(Allocation, cls).create(values)
+        allocation.work.reset_leveling()
+        allocation.work.compute_dates()
 
-    def create(self, values):
-        work_obj = Pool().get('project.work')
-        allocation_id = super(Allocation, self).create(values)
-        allocation = self.browse(allocation_id)
-        work_obj.reset_leveling(allocation.work.id)
-        work_obj.compute_dates(allocation.work.id)
+    @classmethod
+    def delete(cls, allocations):
+        works = [a.work for a in allocations]
+        super(Allocation, cls).delete(allocations)
 
-    def delete(self, ids):
-        work_obj = Pool().get('project.work')
-        allocations = self.browse(ids)
-        work_ids = [a.work.id for a in allocations]
-        res = super(Allocation, self).delete(ids)
-
-        for work_id in work_ids:
-            work_obj.reset_leveling(work_id)
-        for work_id in work_ids:
-            work_obj.compute_dates(work_id)
-
-        return res
-
-Allocation()
+        for work in works:
+            work.reset_leveling()
+        for work in works:
+            work.compute_dates()
diff -r d86297b89cb9 trytond/modules/project_plan/work.py
--- a/trytond/modules/project_plan/work.py	Thu Jul 26 08:06:23 2012 +0530
+++ b/trytond/modules/project_plan/work.py	Mon Sep 24 20:36:41 2012 +0200
@@ -3,20 +3,23 @@
 import datetime
 from collections import deque, defaultdict
 from heapq import heappop, heappush
-from trytond.model import ModelView, ModelSQL, fields
+
+from trytond.model import ModelSQL, fields
 from trytond.wizard import Wizard, StateTransition
 from trytond.pyson import Eval
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
+
+__all__ = ['Work', 'PredecessorSuccessor', 'Leveling']
+__metaclass__ = PoolMeta
 
 
 def intfloor(x):
     return int(round(x, 4))
 
 
-class Work(ModelSQL, ModelView):
-    _name = 'project.work'
-
+class Work:
+    __name__ = 'project.work'
     predecessors = fields.Many2Many('project.predecessor_successor',
         'successor', 'predecessor', 'Predecessors',
         domain=[
@@ -67,47 +70,45 @@
     requests = fields.Function(fields.One2Many('res.request', None,
         'Requests'), 'get_function_fields', setter='set_function_fields')
 
-    def __init__(self):
-        super(Work, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Work, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_dependency'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
                 'recursive_dependency': 'You can not create recursive '
                         'dependencies!',
                 })
 
-    def check_recursion(self, ids, parent='parent'):
-        return super(Work, self).check_recursion(ids, parent='successors')
+    @classmethod
+    def check_recursion(cls, records, parent='successors'):
+        return super(Work, cls).check_recursion(records, parent=parent)
 
-    def get_function_fields(self, ids, names):
+    @classmethod
+    def get_function_fields(cls, works, names):
         '''
         Function to compute function fields
-
-        :param ids: the ids of the works
-        :param names: the list of field name to compute
-        :return: a dictionary with all field names as key and
-            a dictionary as value with id as key
         '''
-        req_ref_obj = Pool().get('res.request.reference')
+        RequestReference = Pool().get('res.request.reference')
 
         cursor = Transaction().cursor
 
         res = {}
 
+        ids = [w.id for w in works]
         if 'requests' in names:
             requests = dict((i, []) for i in ids)
 
             for i in range(0, len(ids), cursor.IN_MAX):
                 sub_ids = ids[i:i + cursor.IN_MAX]
 
-                req_ref_ids = req_ref_obj.search([
+                req_refs = RequestReference.search([
                         ('reference', 'in', [
                                 'project.work,%s' % i for i in sub_ids
                                 ]
                          ),
                         ])
-                req_refs = req_ref_obj.browse(req_ref_ids)
                 for req_ref in req_refs:
                     _, work_id = req_ref.reference.split(',')
                     requests[int(work_id)].append(req_ref.request.id)
@@ -115,17 +116,15 @@
             res['requests'] = requests
 
         if 'duration' in names:
-            all_ids = self.search([
+            all_works = cls.search([
                     ('parent', 'child_of', ids),
-                    ('active', '=', True)]) + ids
-            all_ids = list(set(all_ids))
-
-            works = self.browse(all_ids)
+                    ('active', '=', True)]) + works
+            all_works = list(set(all_works))
 
             durations = {}
             id2work = {}
             leafs = set()
-            for work in works:
+            for work in all_works:
                 id2work[work.id] = work
                 if not work.children:
                     leafs.add(work.id)
@@ -159,25 +158,23 @@
                   'actual_start_time', 'actual_finish_time',
                   'constraint_start_time', 'constraint_finish_time')
 
-        works = None
         for fun_field, db_field in zip(fun_fields, db_fields):
             if fun_field in names:
                 values = {}
-                if works is None:
-                    works = self.browse(ids)
                 for work in works:
-                    values[work.id] = work[db_field] \
-                        and work[db_field].date() or None
+                    values[work.id] = getattr(work, db_field) \
+                        and getattr(work, db_field).date() or None
                 res[fun_field] = values
 
         return res
 
-    def set_function_fields(self, ids, name, value):
-        request_obj = Pool().get('res.request')
-        req_ref_obj = Pool().get('res.request.reference')
+    @classmethod
+    def set_function_fields(cls, works, name, value):
+        pool = Pool()
+        Request = pool.get('res.request')
+        RequestReference = pool.get('res.request.reference')
 
         if name == 'requests':
-            works = self.browse(ids)
             currents = dict((req.id, req) for work in works for req in
                     work.requests)
             if not value:
@@ -189,21 +186,19 @@
 
                 target_ids = len(v) > 1 and v[1] or []
                 if operator == 'create':
-                    to_link.append(request_obj.create(v[1]))
+                    to_link.append(Request.create(v[1]).id)
                 elif operator == 'write':
-                    request_obj.write(v[1], v[2])
+                    Request.write([Request(v[1])], v[2])
                 elif operator == 'delete':
-                    request_obj.delete(v[1])
+                    Request.delete([Request(v[1])])
                 elif operator == 'delete_all':
                     target_ids = []
-                    for record_id in ids:
-                        ref_ids = req_ref_obj.search([
-                                ('reference', '=',
-                                    'project.work,%s' % record_id),
+                    for work in works:
+                        refs = RequestReference.search([
+                                ('reference', '=', str(work)),
                                 ])
-                        refs = req_ref_obj.browse(ref_ids)
                         target_ids.extend(ref.request.id for ref in refs)
-                    request_obj.delete(target_ids)
+                    Request.delete(Request.browse(target_ids))
                 elif operator == 'unlink':
                     to_unlink.extend((i for i in target_ids if i in currents))
                 elif operator == 'add':
@@ -219,19 +214,19 @@
                 else:
                     raise Exception('Operation not supported')
 
-                req_ref_ids = []
+                req_refs = []
                 for i in to_unlink:
                     request = currents[i]
                     for ref in request.references:
-                        if int(ref.reference.split(',')[1]) in ids:
-                            req_ref_ids.append(ref.id)
-                req_ref_obj.delete(req_ref_ids)
+                        if ref.reference in works:
+                            req_refs.append(ref)
+                RequestReference.delete(req_refs)
 
                 for i in to_link:
-                    for record_id in ids:
-                        req_ref_obj.create({
+                    for work in works:
+                        RequestReference.create({
                                 'request': i,
-                                'reference': 'project.work,%s' % record_id,
+                                'reference': str(work),
                                 })
             return
 
@@ -241,7 +236,7 @@
                      'constraint_start_time', 'constraint_finish_time')
         for fun_field, db_field in zip(fun_fields, db_fields):
             if fun_field == name:
-                self.write(ids, {
+                cls.write(works, {
                         db_field: value \
                                 and datetime.datetime.combine(value,
                                     datetime.time()) \
@@ -249,13 +244,14 @@
                         })
                 break
 
-    def add_minutes(self, company, date, minutes):
+    @classmethod
+    def add_minutes(cls, company, date, minutes):
         minutes = int(round(minutes))
         minutes = date.minute + minutes
 
         hours = minutes // 60
         if hours:
-            date = self.add_hours(company, date, hours)
+            date = cls.add_hours(company, date, hours)
 
         minutes = minutes % 60
 
@@ -269,17 +265,18 @@
 
         return date
 
-    def add_hours(self, company, date, hours):
+    @classmethod
+    def add_hours(cls, company, date, hours):
         while hours:
             if hours != intfloor(hours):
                 minutes = (hours - intfloor(hours)) * 60
-                date = self.add_minutes(company, date, minutes)
+                date = cls.add_minutes(company, date, minutes)
             hours = intfloor(hours)
 
             hours = date.hour + hours
             days = hours // company.hours_per_work_day
             if days:
-                date = self.add_days(company, date, days)
+                date = cls.add_days(company, date, days)
 
             hours = hours % company.hours_per_work_day
 
@@ -295,13 +292,14 @@
 
         return date
 
-    def add_days(self, company, date, days):
+    @classmethod
+    def add_days(cls, company, date, days):
         day_per_week = company.hours_per_work_week / company.hours_per_work_day
 
         while days:
             if days != intfloor(days):
                 hours = (days - intfloor(days)) * company.hours_per_work_day
-                date = self.add_hours(company, date, hours)
+                date = cls.add_hours(company, date, hours)
             days = intfloor(days)
 
             days = date.weekday() + days
@@ -310,7 +308,7 @@
             days = days % day_per_week
 
             if weeks:
-                date = self.add_weeks(company, date, weeks)
+                date = cls.add_weeks(company, date, weeks)
 
             date += datetime.timedelta(days=-date.weekday() + intfloor(days))
 
@@ -318,39 +316,39 @@
 
         return date
 
-    def add_weeks(self, company, date, weeks):
+    @classmethod
+    def add_weeks(cls, company, date, weeks):
         day_per_week = company.hours_per_work_week / company.hours_per_work_day
 
         if weeks != intfloor(weeks):
             days = (weeks - intfloor(weeks)) * day_per_week
             if days:
-                date = self.add_days(company, date, days)
+                date = cls.add_days(company, date, days)
 
         date += datetime.timedelta(days=7 * intfloor(weeks))
 
         return date
 
-    def compute_dates(self, work_id):
-        active_work = self.browse(work_id)
+    def compute_dates(self):
         values = {}
         get_early_finish = lambda work: values.get(work, {}).get(
-            'early_finish_time', work['early_finish_time'])
+            'early_finish_time', work.early_finish_time)
         get_late_start = lambda work: values.get(work, {}).get(
-            'late_start_time', work['late_start_time'])
+            'late_start_time', work.late_start_time)
         maxdate = lambda x, y: x and y and max(x, y) or x or y
         mindate = lambda x, y: x and y and min(x, y) or x or y
 
         # propagate constraint_start_time
         constraint_start = reduce(maxdate, (pred.early_finish_time \
-                for pred in active_work.predecessors), None)
+                for pred in self.predecessors), None)
 
-        if constraint_start is None and active_work.parent:
-            constraint_start = active_work.parent.early_start_time
+        if constraint_start is None and self.parent:
+            constraint_start = self.parent.early_start_time
 
         constraint_start = maxdate(constraint_start,
-                                   active_work.constraint_start_time)
+            self.constraint_start_time)
 
-        works = deque([(active_work, constraint_start)])
+        works = deque([(self, constraint_start)])
         work2children = {}
         parent = None
 
@@ -423,15 +421,15 @@
 
         # propagate constraint_finish_time
         constraint_finish = reduce(mindate, (succ.late_start_time \
-                for succ in active_work.successors), None)
+                for succ in self.successors), None)
 
-        if constraint_finish is None and active_work.parent:
-            constraint_finish = active_work.parent.late_finish_time
+        if constraint_finish is None and self.parent:
+            constraint_finish = self.parent.late_finish_time
 
         constraint_finish = mindate(constraint_finish,
-                                    active_work.constraint_finish_time)
+            self.constraint_finish_time)
 
-        works = deque([(active_work, constraint_finish)])
+        works = deque([(self, constraint_finish)])
         work2children = {}
         parent = None
 
@@ -507,31 +505,29 @@
         for work, val in values.iteritems():
             write_cond = False
             for field in write_fields:
-                if field in val and work[field] != val[field]:
+                if field in val and getattr(work, field) != val[field]:
                     write_cond = True
                     break
 
             if write_cond:
-                self.write(work.id, val)
+                self.write([work], val)
 
-    def reset_leveling(self, work_id):
+    def reset_leveling(self):
         get_key = lambda w: (set(p.id for p in w.predecessors),
                              set(s.id for s in w.successors))
 
-        work = self.browse(work_id)
-        parent_id = work.parent and work.parent.id or None
-        sibling_ids = self.search([
+        parent_id = self.parent and self.parent.id or None
+        siblings = self.search([
                 ('parent', '=', parent_id)
                 ])
-        siblings = self.browse(sibling_ids)
         to_clean = []
 
-        ref_key = get_key(work)
+        ref_key = get_key(self)
         for sibling in siblings:
             if sibling.leveling_delay == sibling.back_leveling_delay == 0:
                 continue
             if get_key(sibling) == ref_key:
-                to_clean.append(sibling.id)
+                to_clean.append(sibling)
 
         if to_clean:
             self.write(to_clean, {
@@ -539,7 +535,7 @@
                     'back_leveling_delay': 0,
                     })
 
-    def create_leveling(self, work_id):
+    def create_leveling(self):
         # define some helper functions
         get_key = lambda w: (set(p.id for p in w.predecessors),
                              set(s.id for s in w.successors))
@@ -595,140 +591,120 @@
 
                 yield sibling, delay
 
-        work = self.browse(work_id)
-        parent_id = work.parent and work.parent.id or None
-        sibling_ids = self.search([
-                ('parent', '=', parent_id)
+        parent = self.parent and self.parent.id or None
+        siblings = self.search([
+                ('parent', '=', parent.id)
                 ])
 
-        refkey = get_key(work)
-        siblings = [s for s in self.browse(sibling_ids)
-            if get_key(s) == refkey]
+        refkey = get_key(self)
+        siblings = [s for s in siblings if get_key(s) == refkey]
 
         for sibling, delay in compute_delays(siblings):
-            self.write(sibling.id, {
+            self.write([sibling], {
                     'leveling_delay': delay,
                     })
 
         siblings.reverse()
         for sibling, delay in compute_delays(siblings):
-            self.write(sibling.id, {
+            self.write([sibling], {
                     'back_leveling_delay': delay,
                     })
 
-        if parent_id:
-            self.compute_dates(parent_id)
+        if parent:
+            parent.compute_dates()
 
-    def write(self, ids, values):
-        res = super(Work, self).write(ids, values)
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def write(cls, works, values):
+        super(Work, cls).write(works, values)
 
         if 'effort' in values:
-            for work_id in ids:
-                self.reset_leveling(work_id)
+            for work in works:
+                work.reset_leveling()
         fields = ('constraint_start_time', 'constraint_finish_time',
                   'effort')
         if reduce(lambda x, y: x or y in values, fields, False):
-            for work_id in ids:
-                self.compute_dates(work_id)
-        return res
+            for work in works:
+                work.compute_dates()
 
-    def create(self, values):
-        work_id = super(Work, self).create(values)
-        self.reset_leveling(work_id)
-        self.compute_dates(work_id)
-        return work_id
+    @classmethod
+    def create(cls, values):
+        work = super(Work, cls).create(values)
+        work.reset_leveling()
+        work.compute_dates()
+        return work
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        works = self.browse(ids)
+    @classmethod
+    def delete(cls, works):
         to_update = set()
         for work in works:
-            if work.parent and work.parent.id not in ids:
-                to_update.add(work.parent.id)
-                to_update.update(c.id for c in work.parent.children \
-                                     if c.id not in ids)
-        res = super(Work, self).delete(ids)
+            if work.parent and work.parent not in works:
+                to_update.add(work.parent)
+                to_update.update(c for c in work.parent.children
+                    if c not in works)
+        super(Work, cls).delete(works)
 
-        for work_id in to_update:
-            self.reset_leveling(work_id)
-            self.compute_dates(work_id)
-
-        return res
-Work()
+        for work in to_update:
+            work.reset_leveling()
+            work.compute_dates()
 
 
 class PredecessorSuccessor(ModelSQL):
     'Predecessor - Successor'
-    _name = 'project.predecessor_successor'
-    _description = __doc__
-
+    __name__ = 'project.predecessor_successor'
     predecessor = fields.Many2One('project.work', 'Predecessor',
             ondelete='CASCADE', required=True, select=True)
     successor = fields.Many2One('project.work', 'Successor',
             ondelete='CASCADE', required=True, select=True)
 
-    def write(self, ids, values):
-        work_obj = Pool().get('project.work')
-        res = super(PredecessorSuccessor, self).write(ids, values)
+    @classmethod
+    def write(cls, pred_succs, values):
+        Work = Pool().get('project.work')
+        super(PredecessorSuccessor, cls).write(pred_succs, values)
 
-        for work_id in values.itervalues():
-            work_obj.reset_leveling(work_id)
-        for work_id in values.itervalues():
-            work_obj.compute_dates(work_id)
-        return res
+        works = Work.browse(values.itervalues())
+        for work in works:
+            work.reset_leveling()
+        for work in works:
+            work.compute_dates()
 
-    def delete(self, ids):
-        work_obj = Pool().get('project.work')
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        work_ids = set()
-        parent_ids = set()
-        pred_succs = self.browse(ids)
+    @classmethod
+    def delete(cls, pred_succs):
+        works = set()
+        parents = set()
         for pred_succ in pred_succs:
-            work_ids.update((pred_succ.predecessor.id,
-                             pred_succ.successor.id))
+            works.update((pred_succ.predecessor,
+                    pred_succ.successor))
 
             if pred_succ.predecessor.parent:
-                parent_ids.add(pred_succ.predecessor.parent.id)
+                parents.add(pred_succ.predecessor.parent)
 
-        res = super(PredecessorSuccessor, self).delete(ids)
+        super(PredecessorSuccessor, cls).delete(pred_succs)
 
-        for work_id in work_ids:
-            work_obj.reset_leveling(work_id)
+        for work in works:
+            work.reset_leveling()
 
-        for parent_id in parent_ids:
-            work_obj.compute_dates(parent_id)
+        for parent in parents:
+            parent.compute_dates()
 
-        return res
+    @classmethod
+    def create(cls, values):
+        pred_succ = super(PredecessorSuccessor, cls).create(values)
 
-    def create(self, values):
-        work_obj = Pool().get('project.work')
-        ps_id = super(PredecessorSuccessor, self).create(values)
-
-        pred_succ = self.browse(ps_id)
-        work_obj.reset_leveling(pred_succ.predecessor.id)
-        work_obj.reset_leveling(pred_succ.successor.id)
+        pred_succ.predecessor.reset_leveling()
+        pred_succ.successor.reset_leveling()
 
         if pred_succ.predecessor.parent:
-            work_obj.compute_dates(pred_succ.predecessor.parent.id)
-        return id
-
-PredecessorSuccessor()
+            pred_succ.predecessor.parent.compute_dates()
+        return pred_succ
 
 
 class Leveling(Wizard):
     'Tasks Leveling'
-    _name = 'project_plan.work.leveling'
+    __name__ = 'project_plan.work.leveling'
     start_state = 'leveling'
     leveling = StateTransition()
 
-    def transition_leveling(self, session):
-        work_obj = Pool().get('project.work')
-        work_obj.create_leveling(Transaction().context['active_id'])
+    def transition_leveling(self):
+        Work = Pool().get('project.work')
+        Work(Transaction().context['active_id']).create_leveling()
         return 'end'
-
-Leveling()
diff -r fee62166618f trytond/modules/project_revenue/__init__.py
--- a/trytond/modules/project_revenue/__init__.py	Fri Sep 21 15:40:29 2012 +0200
+++ b/trytond/modules/project_revenue/__init__.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,5 +1,15 @@
 #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.pool import Pool
 from .service import *
 from .work import *
+
+
+def register():
+    Pool.register(
+        Employee,
+        EmployeeCostPrice,
+        TimesheetLine,
+        Work,
+        module='project_revenue', type_='model')
diff -r fee62166618f trytond/modules/project_revenue/service.py
--- a/trytond/modules/project_revenue/service.py	Fri Sep 21 15:40:29 2012 +0200
+++ b/trytond/modules/project_revenue/service.py	Mon Sep 24 20:36:41 2012 +0200
@@ -2,56 +2,54 @@
 #this repository contains the full copyright notices and license terms.
 from decimal import Decimal
 from trytond.model import ModelView, ModelSQL, fields
-from trytond.model.cacheable import Cacheable
 from trytond.pyson import Eval
+from trytond.cache import Cache
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Employee', 'EmployeeCostPrice']
+__metaclass__ = PoolMeta
 
-class Employee(ModelSQL, ModelView, Cacheable):
-    _name = 'company.employee'
 
+class Employee:
+    __name__ = 'company.employee'
     cost_price = fields.Function(fields.Numeric('Cost Price',
         digits=(16, Eval('currency_digits', 2)), depends=['currency_digits'],
         help="Hourly cost price for this Employee"), 'get_cost_price')
     cost_prices = fields.One2Many('company.employee_cost_price', 'employee',
             'Cost Prices', help="List of hourly cost price over time")
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['company']), 'get_currency_digits')
+            on_change_with=['company']), 'on_change_with_currency_digits')
+    _cost_prices_cache = Cache('company_employee.cost_prices')
 
-    def get_cost_price(self, ids, name):
+    def get_cost_price(self, name):
         '''
         Return the cost price at the date given in the context or the
         current date
         '''
+        ctx_date = Transaction().context.get('date', None)
+        return self.compute_cost_price(ctx_date)
 
-        res = {}
-        ctx_date = Transaction().context.get('date', None)
-
-        for employee_id in ids:
-            res[employee_id] = self.compute_cost_price(employee_id, ctx_date)
-        return res
-
-    def compute_cost_price(self, employee_id, date=None):
-        date_obj = Pool().get('ir.date')
-        cost_price_obj = Pool().get('company.employee_cost_price')
+    def compute_cost_price(self, date=None):
+        pool = Pool()
+        Date = pool.get('ir.date')
+        CostPrice = pool.get('company.employee_cost_price')
 
         # Get from cache employee costs or fetch them from the db
-        employee_costs = self.get(employee_id)
+        employee_costs = self._cost_prices_cache.get(self.id)
         if employee_costs is None:
-            cost_price_ids = cost_price_obj.search([
-                    ('employee', '=', employee_id),
+            cost_prices = CostPrice.search([
+                    ('employee', '=', self.id),
                     ], order=[('date', 'ASC')])
-            cost_prices = cost_price_obj.browse(cost_price_ids)
 
             employee_costs = []
             for cost_price in cost_prices:
                 employee_costs.append(
                     (cost_price.date, cost_price.cost_price))
-            self.add(employee_id, employee_costs)
+            self._cost_prices_cache.set(self.id, employee_costs)
 
         if date is None:
-            date = date_obj.today()
+            date = Date.today()
         # compute the cost price for the given date
         cost = 0
         if employee_costs and date >= employee_costs[0][0]:
@@ -61,34 +59,24 @@
                     break
         return cost
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for employee in self.browse(ids):
-            res[employee.id] = employee.company.currency.digits
-        return res
+    def on_change_with_currency_digits(self, name=None):
+        if self.company:
+            return self.company.currency.digits
+        return 2
 
-    def on_change_with_currency_digits(self, vals):
-        company_obj = Pool().get('company.company')
-        if vals.get('company'):
-            company = company_obj.browse(vals['company'])
+    @staticmethod
+    def default_currency_digits():
+        Company = Pool().get('company.company')
+        company = Transaction().context.get('company')
+        if company:
+            company = Company(company)
             return company.currency.digits
         return 2
 
-    def default_currency_digits(self):
-        company_obj = Pool().get('company.company')
-        company = Transaction().context.get('company')
-        if company:
-            company = company_obj.browse(company)
-            return company.currency.digits
-        return 2
-
-Employee()
-
 
 class EmployeeCostPrice(ModelSQL, ModelView):
     'Employee Cost Price'
-    _name = 'company.employee_cost_price'
-    _description = __doc__
+    __name__ = 'company.employee_cost_price'
     _rec_name = 'date'
     date = fields.Date('Date', required=True, select=True)
     cost_price = fields.Numeric('Cost Price',
@@ -97,54 +85,55 @@
             help="Hourly cost price")
     employee = fields.Many2One('company.employee', 'Employee')
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['employee']), 'get_currency_digits')
+            on_change_with=['employee']), 'on_change_with_currency_digits')
 
-    def __init__(self):
-        super(EmployeeCostPrice, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(EmployeeCostPrice, cls).__setup__()
+        cls._sql_constraints = [
             ('date_cost_price_uniq', 'UNIQUE(date, cost_price)',
                 'A employee can only have one cost price by date!'),
-        ]
-        self._order.insert(0, ('date', 'DESC'))
+            ]
+        cls._order.insert(0, ('date', 'DESC'))
 
-    def default_cost_price(self):
+    @staticmethod
+    def default_cost_price():
         return Decimal(0)
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
-    def delete(self, ids):
-        Pool().get('company.employee').clear()
-        return super(EmployeeCostPrice, self).delete(ids)
+    @classmethod
+    def delete(cls, prices):
+        Employee = Pool().get('company.employee')
+        super(EmployeeCostPrice, cls).delete(prices)
+        Employee._cost_prices_cache.clear()
 
-    def create(self, vals):
-        Pool().get('company.employee').clear()
-        return super(EmployeeCostPrice, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        Employee = Pool().get('company.employee')
+        price = super(EmployeeCostPrice, cls).create(vals)
+        Employee._cost_prices_cache.clear()
+        return price
 
-    def write(self, ids, vals):
-        Pool().get('company.employee').clear()
-        return super(EmployeeCostPrice, self).write(ids, vals)
+    @classmethod
+    def write(cls, prices, vals):
+        Employee = Pool().get('company.employee')
+        super(EmployeeCostPrice, cls).write(prices, vals)
+        Employee._cost_prices_cache.clear()
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for costprice in self.browse(ids):
-            res[costprice.id] = costprice.employee.company.currency.digits
-        return res
-
-    def on_change_with_currency_digits(self, vals):
-        employee_obj = Pool().get('company.employee')
-        if vals.get('employee'):
-            employee = employee_obj.browse(vals['employee'])
-            return employee.company.currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.employee:
+            return self.employee.company.currency.digits
         return 2
 
-    def default_currency_digits(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency_digits():
+        Company = Pool().get('company.company')
         company = Transaction().context.get('company')
         if company:
-            company = company_obj.browse(company)
+            company = Company(company)
             return company.currency.digits
         return 2
-
-EmployeeCostPrice()
diff -r fee62166618f trytond/modules/project_revenue/work.py
--- a/trytond/modules/project_revenue/work.py	Fri Sep 21 15:40:29 2012 +0200
+++ b/trytond/modules/project_revenue/work.py	Mon Sep 24 20:36:41 2012 +0200
@@ -1,39 +1,36 @@
 #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 trytond.model import ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['TimesheetLine', 'Work']
+__metaclass__ = PoolMeta
 
-class TimesheetLine(ModelSQL, ModelView):
-    _name = 'timesheet.line'
 
-    def compute_cost(self, line):
-        employee_obj = Pool().get('company.employee')
-        currency_obj = Pool().get('currency.currency')
+class TimesheetLine:
+    __name__ = 'timesheet.line'
 
-        cost_price = employee_obj.compute_cost_price(line.employee.id,
-                date=line.date)
+    def compute_cost(self):
+        Currency = Pool().get('currency.currency')
 
-        line_company = line.employee.company
-        work_company = line.work.company
-        if line_company.id != work_company.id and\
-                line_company.currency.id != work_company.currency.id:
-            with Transaction().set_context(date=line.date):
-                cost_price = currency_obj.compute(line_company.currency.id,
-                    cost_price, work_company.currency.id)
+        cost_price = self.employee.compute_cost_price(date=self.date)
 
-        return Decimal(str(line.hours)) * cost_price
+        line_company = self.employee.company
+        work_company = self.work.company
+        if (line_company != work_company and
+                line_company.currency != work_company.currency):
+            with Transaction().set_context(date=self.date):
+                cost_price = Currency.compute(line_company.currency,
+                    cost_price, work_company.currency)
 
-TimesheetLine()
+        return Decimal(str(self.hours)) * cost_price
 
 
-class Work(ModelSQL, ModelView):
-    'Work Effort'
-    _name = 'project.work'
-
+class Work:
+    __name__ = 'project.work'
     product = fields.Many2One('product.product', 'Product',
         states={
             'invisible': ~Eval('timesheet_available'),
@@ -52,25 +49,25 @@
             digits=(16, Eval('currency_digits', 2)),
             depends=['currency_digits', 'timesheet_available']), 'get_cost')
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['company']), 'get_currency_digits')
+            on_change_with=['company']), 'on_change_with_currency_digits')
 
-    def get_cost(self, ids, name):
-        timesheet_line_obj = Pool().get('timesheet.line')
-        all_ids = self.search([
-                ('parent', 'child_of', ids),
-                ('active', '=', True)]) + ids
-        all_ids = list(set(all_ids))
-        res = {}
+    @classmethod
+    def get_cost(cls, works, name):
+        all_works = cls.search([
+                ('parent', 'child_of', [w.id for w in works]),
+                ('active', '=', True)]) + works
+        all_works = list(set(all_works))
+        costs = {}
         id2work = {}
         leafs = set()
-        for work in self.browse(all_ids):
+        for work in all_works:
             id2work[work.id] = work
             if not work.children:
                 leafs.add(work.id)
 
-            res[work.id] = Decimal('0')
+            costs[work.id] = Decimal('0')
             for ts_line in work.timesheet_lines:
-                res[work.id] += timesheet_line_obj.compute_cost(ts_line)
+                costs[work.id] += ts_line.compute_cost()
 
         while leafs:
             parents = set()
@@ -78,35 +75,36 @@
                 work = id2work[work_id]
                 if not work.active:
                     continue
-                if work.parent and work.parent.id in res:
-                    res[work.parent.id] += res[work_id]
+                if work.parent and work.parent.id in costs:
+                    costs[work.parent.id] += costs[work_id]
                     parents.add(work.parent.id)
             leafs = parents
 
-        for id in all_ids:
-            if id not in ids:
-                del res[id]
+        for work in all_works:
+            if work not in works:
+                del costs[work.id]
 
-        return res
+        return costs
 
-    def get_revenue(self, ids, name):
-        all_ids = self.search([
-                ('parent', 'child_of', ids),
-                ('active', '=', True)]) + ids
-        all_ids = list(set(all_ids))
-        res = {}
+    @classmethod
+    def get_revenue(cls, works, name):
+        all_works = cls.search([
+                ('parent', 'child_of', [w.id for w in works]),
+                ('active', '=', True)]) + works
+        all_works = list(set(all_works))
+        revenues = {}
         id2work = {}
         leafs = set()
-        for work in self.browse(all_ids):
+        for work in all_works:
             id2work[work.id] = work
             if not work.children:
                 leafs.add(work.id)
 
             if work.type == 'task' and work.list_price:
-                res[work.id] = (work.list_price
+                revenues[work.id] = (work.list_price
                     * Decimal(str(work.total_effort)))
             else:
-                res[work.id] = Decimal('0')
+                revenues[work.id] = Decimal('0')
 
         while leafs:
             parents = set()
@@ -114,71 +112,57 @@
                 work = id2work[work_id]
                 if not work.active:
                     continue
-                if work.parent and work.parent.id in res:
-                    res[work.parent.id] += res[work_id]
+                if work.parent and work.parent.id in revenues:
+                    revenues[work.parent.id] += revenues[work_id]
                     parents.add(work.parent.id)
             leafs = parents
 
-        for id in all_ids:
-            if id not in ids:
-                del res[id]
+        for work in all_works:
+            if work not in works:
+                del revenues[work.id]
 
-        return res
+        return revenues
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for work in self.browse(ids):
-            res[work.id] = work.company.currency.digits
-        return res
+    def on_change_with_currency_digits(self, name=None):
+        if self.company:
+            return self.company.currency.digits
+        return 2
 
-    def on_change_with_currency_digits(self, vals):
-        company_obj = Pool().get('company.company')
-        if vals.get('company'):
-            company = company_obj.browse(vals['company'])
+    @staticmethod
+    def default_currency_digits():
+        Company = Pool().get('company.company')
+        company = Transaction().context.get('company')
+        if company:
+            company = Company(company)
             return company.currency.digits
         return 2
 
-    def default_currency_digits(self):
-        company_obj = Pool().get('company.company')
-        company = Transaction().context.get('company')
-        if company:
-            company = company_obj.browse(company)
-            return company.currency.digits
-        return 2
+    def on_change_product(self):
+        pool = Pool()
+        User = pool.get('res.user')
+        ModelData = pool.get('ir.model.data')
+        Uom = pool.get('product.uom')
+        Currency = pool.get('currency.currency')
 
-    def on_change_product(self, vals):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        user_obj = pool.get('res.user')
-        company_obj = pool.get('company.company')
-        model_data_obj = pool.get('ir.model.data')
-        uom_obj = pool.get('product.uom')
-        currency_obj = pool.get('currency.currency')
-
-        if not vals.get('product'):
+        if not self.product:
             return {}
 
         context = {}
 
-        product = product_obj.browse(vals['product'])
+        if self.party:
+            context['customer'] = self.party.id
 
-        if vals.get('party'):
-            context['customer'] = vals['party']
-
-        uom_id = model_data_obj.get_id('product', 'uom_hour')
-        hour_uom = uom_obj.browse(uom_id)
+        hour_uom = Uom(ModelData.get_id('product', 'uom_hour'))
 
         with Transaction().set_context(context):
-            list_price = uom_obj.compute_price(product.default_uom,
-                    product.list_price, hour_uom)
+            list_price = Uom.compute_price(self.product.default_uom,
+                self.product.list_price, hour_uom)
 
-        if vals.get('company'):
-            user = user_obj.browse(Transaction().user)
-            if user.company.id != vals['company']:
-                company = company_obj.browse(vals['company'])
-                if user.company.currency.id != company.currency.id:
-                    list_price = currency_obj.compute(user.company.currency,
-                            list_price, company.currency)
+        if self.company:
+            user = User(Transaction().user)
+            if user.company != self.company:
+                if user.company.currency != self.company.currency:
+                    list_price = Currency.compute(user.company.currency,
+                        list_price, self.company.currency)
 
         return {'list_price': list_price}
-Work()
diff -r 5e0d53cac617 trytond/modules/purchase/__init__.py
--- a/trytond/modules/purchase/__init__.py	Mon Sep 10 15:28:28 2012 +0200
+++ b/trytond/modules/purchase/__init__.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,7 +1,42 @@
 #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.pool import Pool
 from .purchase import *
 from .configuration import *
 from .invoice import *
 from .stock import *
+
+
+def register():
+    Pool.register(
+        Purchase,
+        PurchaseInvoice,
+        PurchaseIgnoredInvoice,
+        PurchaseRecreadtedInvoice,
+        PurchaseLine,
+        PurchaseLineTax,
+        PurchaseLineInvoiceLine,
+        PurchaseLineIgnoredMove,
+        PurchaseLineRecreatedMove,
+        Template,
+        Product,
+        ProductSupplier,
+        ProductSupplierPrice,
+        ShipmentIn,
+        Move,
+        HandleShipmentExceptionAsk,
+        HandleInvoiceExceptionAsk,
+        Configuration,
+        Invoice,
+        InvoiceLine,
+        module='purchase', type_='model')
+    Pool.register(
+        PurchaseReport,
+        module='purchase', type_='report')
+    Pool.register(
+        OpenSupplier,
+        HandleShipmentException,
+        HandleInvoiceException,
+        OpenProductQuantitiesByWarehouse,
+        module='purchase', type_='wizard')
diff -r 5e0d53cac617 trytond/modules/purchase/configuration.py
--- a/trytond/modules/purchase/configuration.py	Mon Sep 10 15:28:28 2012 +0200
+++ b/trytond/modules/purchase/configuration.py	Mon Sep 24 20:36:42 2012 +0200
@@ -3,12 +3,12 @@
 from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
 from trytond.pyson import Eval, Bool
 
+__all__ = ['Configuration']
+
 
 class Configuration(ModelSingleton, ModelSQL, ModelView):
     'Purchase Configuration'
-    _name = 'purchase.configuration'
-    _description = __doc__
-
+    __name__ = 'purchase.configuration'
     purchase_sequence = fields.Property(fields.Many2One('ir.sequence',
             'Purchase Reference Sequence', domain=[
                 ('company', 'in',
@@ -22,5 +22,3 @@
                 ], 'Invoice Method', states={
                 'required': Bool(Eval('context', {}).get('company', 0)),
                 }))
-
-Configuration()
diff -r 5e0d53cac617 trytond/modules/purchase/invoice.py
--- a/trytond/modules/purchase/invoice.py	Mon Sep 10 15:28:28 2012 +0200
+++ b/trytond/modules/purchase/invoice.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,50 +1,110 @@
 #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 Model, fields
-from trytond.pool import Pool
+from trytond.model import ModelView, Workflow, fields
+from trytond.pool import Pool, PoolMeta
+from trytond.transaction import Transaction
 
+__all__ = ['Invoice', 'InvoiceLine']
+__metaclass__ = PoolMeta
 
-class Invoice(Model):
-    _name = 'account.invoice'
 
+class Invoice:
+    __name__ = 'account.invoice'
+    purchase_exception_state = fields.Function(fields.Selection([
+        ('', ''),
+        ('ignored', 'Ignored'),
+        ('recreated', 'Recreated'),
+        ], 'Exception State'), 'get_purchase_exception_state')
     purchases = fields.Many2Many('purchase.purchase-account.invoice',
             'invoice', 'purchase', 'Purchases', readonly=True)
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def __setup__(cls):
+        super(Invoice, cls).__setup__()
+        cls._error_messages.update({
+                'delete_purchase_invoice': 'You can not delete invoices ' \
+                    'that come from a purchase!',
+                'reset_invoice_purchase': 'You cannot reset to draft ' \
+                        'an invoice generated by a purchase.',
+                })
+
+    @classmethod
+    def get_purchase_exception_state(cls, invoices, name):
+        Purchase = Pool().get('purchase.purchase')
+        purchases = Purchase.search([
+                ('invoices', 'in', [i.id for i in invoices]),
+                ])
+
+        recreated = tuple(i for p in purchases for i in p.invoices_recreated)
+        ignored = tuple(i for p in purchases for i in p.invoices_ignored)
+
+        res = {}
+        for invoice in invoices:
+            if invoice in recreated:
+                res[invoice.id] = 'recreated'
+            elif invoice in ignored:
+                res[invoice.id] = 'ignored'
+            else:
+                res[invoice.id] = ''
+        return res
+
+    @classmethod
+    def delete(cls, invoices):
+        cursor = Transaction().cursor
+        if invoices:
+            cursor.execute('SELECT id FROM purchase_invoices_rel ' \
+                'WHERE invoice IN (' + ','.join(('%s',) * len(invoices)) + ')',
+                [i.id for i in invoices])
+        if cursor.fetchone():
+            cls.raise_user_error('delete_purchase_invoice')
+        super(Invoice, cls).delete(invoices)
+
+    @classmethod
+    def copy(cls, invoices, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default.setdefault('purchases', None)
-        return super(Invoice, self).copy(ids, default=default)
+        return super(Invoice, cls).copy(invoices, default=default)
 
-    def paid(self, ids):
+    @classmethod
+    @ModelView.button
+    @Workflow.transition('draft')
+    def draft(cls, invoices):
+        Purchase = Pool().get('purchase.purchase')
+        purchases = Purchase.search([
+                ('invoices', 'in', [i.id for i in invoices]),
+                ])
+
+        if purchases:
+            cls.raise_user_error('reset_invoice_purchase')
+
+        return super(Invoice, cls).draft(invoices)
+
+    @classmethod
+    def paid(cls, invoices):
         pool = Pool()
-        purchase_obj = pool.get('purchase.purchase')
-        super(Invoice, self).paid(ids)
-        invoices = self.browse(ids)
-        purchase_obj.process([p.id for i in invoices for p in i.purchases])
+        Purchase = pool.get('purchase.purchase')
+        super(Invoice, cls).paid(invoices)
+        Purchase.process([p for i in invoices for p in i.purchases])
 
-    def cancel(self, ids):
+    @classmethod
+    def cancel(cls, invoices):
         pool = Pool()
-        purchase_obj = pool.get('purchase.purchase')
-        super(Invoice, self).cancel(ids)
-        invoices = self.browse(ids)
-        purchase_obj.process([p.id for i in invoices for p in i.purchases])
+        Purchase = pool.get('purchase.purchase')
+        super(Invoice, cls).cancel(invoices)
+        Purchase.process([p for i in invoices for p in i.purchases])
 
-Invoice()
 
-
-class InvoiceLine(Model):
-    _name = 'account.invoice.line'
-
+class InvoiceLine:
+    __name__ = 'account.invoice.line'
     purchase_lines = fields.Many2Many('purchase.line-account.invoice.line',
             'invoice_line', 'purchase_line', 'Purchase Lines', readonly=True)
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, lines, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default.setdefault('purchase_lines', None)
-        return super(InvoiceLine, self).copy(ids, default=default)
-
-InvoiceLine()
+        return super(InvoiceLine, cls).copy(lines, default=default)
diff -r 5e0d53cac617 trytond/modules/purchase/purchase.py
--- a/trytond/modules/purchase/purchase.py	Mon Sep 10 15:28:28 2012 +0200
+++ b/trytond/modules/purchase/purchase.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,7 +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.
 import datetime
-import copy
 from decimal import Decimal
 from trytond.model import Workflow, ModelView, ModelSQL, fields
 from trytond.modules.company import CompanyReport
@@ -10,9 +9,18 @@
 from trytond.backend import TableHandler
 from trytond.pyson import Eval, Bool, If, PYSONEncoder, Id
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.config import CONFIG
 
+__all__ = ['Purchase', 'PurchaseInvoice', 'PurchaseIgnoredInvoice',
+    'PurchaseRecreadtedInvoice', 'PurchaseLine', 'PurchaseLineTax',
+    'PurchaseLineInvoiceLine', 'PurchaseLineIgnoredMove',
+    'PurchaseLineRecreatedMove', 'PurchaseReport', 'Template', 'Product',
+    'ProductSupplier', 'ProductSupplierPrice', 'ShipmentIn', 'Move',
+    'OpenSupplier', 'HandleShipmentExceptionAsk', 'HandleShipmentException',
+    'HandleInvoiceExceptionAsk', 'HandleInvoiceException']
+__metaclass__ = PoolMeta
+
 _STATES = {
     'readonly': Eval('state') != 'draft',
     }
@@ -21,10 +29,8 @@
 
 class Purchase(Workflow, ModelSQL, ModelView):
     'Purchase'
-    _name = 'purchase.purchase'
+    __name__ = 'purchase.purchase'
     _rec_name = 'reference'
-    _description = __doc__
-
     company = fields.Many2One('company.company', 'Company', required=True,
         states={
             'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
@@ -57,7 +63,7 @@
             required=True, states=_STATES, on_change=['party', 'payment_term'],
             select=True, depends=_DEPENDS)
     party_lang = fields.Function(fields.Char('Party Language',
-        on_change_with=['party']), 'get_function_fields')
+            on_change_with=['party']), 'on_change_with_party_lang')
     invoice_address = fields.Many2One('party.address', 'Invoice Address',
         domain=[('party', '=', Eval('party'))], states=_STATES,
         depends=['state', 'party'])
@@ -71,7 +77,7 @@
             },
         depends=['state'])
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['currency']), 'get_function_fields')
+            on_change_with=['currency']), 'on_change_with_currency_digits')
     lines = fields.One2Many('purchase.line', 'purchase', 'Lines',
         states=_STATES, on_change=['lines', 'currency', 'party'],
         depends=_DEPENDS)
@@ -121,15 +127,16 @@
             ('exception', 'Exception'),
             ], 'Shipment State', readonly=True, required=True)
     shipments = fields.Function(fields.One2Many('stock.shipment.in', None,
-        'Shipments'), 'get_function_fields')
+            'Shipments'), 'get_shipments')
     moves = fields.Function(fields.One2Many('stock.move', None, 'Moves'),
-            'get_function_fields')
+        'get_moves')
 
-    def __init__(self):
-        super(Purchase, self).__init__()
-        self._order.insert(0, ('purchase_date', 'DESC'))
-        self._order.insert(1, ('id', 'DESC'))
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Purchase, cls).__setup__()
+        cls._order.insert(0, ('purchase_date', 'DESC'))
+        cls._order.insert(1, ('id', 'DESC'))
+        cls._error_messages.update({
                 'invoice_addresse_required': 'Invoice addresses must be '
                 'defined for the quotation.',
                 'warehouse_required': 'A warehouse must be defined for the ' \
@@ -138,8 +145,8 @@
                         'an "Account Payable" on the party "%s"!',
                 'delete_cancel': 'Purchase "%s" must be cancelled before '\
                     'deletion!',
-            })
-        self._transitions |= set((
+                })
+        cls._transitions |= set((
                 ('draft', 'quotation'),
                 ('quotation', 'confirmed'),
                 ('confirmed', 'confirmed'),
@@ -148,7 +155,7 @@
                 ('quotation', 'draft'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': ~Eval('state').in_(['draft', 'quotation']),
                     },
@@ -178,9 +185,10 @@
                     },
                 })
         # The states where amounts are cached
-        self._states_cached = ['confirmed', 'done', 'cancel']
+        cls._states_cached = ['confirmed', 'done', 'cancel']
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         cursor.execute("UPDATE ir_model_data "\
@@ -193,18 +201,18 @@
                 "WHERE (relation like '%%packing%%' "\
                     "OR name like '%%packing%%') AND module = %s",
                 (module_name,))
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('packing_state', 'shipment_state')
 
-        super(Purchase, self).init(module_name)
+        super(Purchase, cls).__register__(module_name)
 
         # Migration from 1.2: rename packing to shipment in
         # invoice_method values
-        cursor.execute("UPDATE " + self._table + " "\
+        cursor.execute("UPDATE " + cls._table + " "\
                 "SET invoice_method = 'shipment' "\
                 "WHERE invoice_method = 'packing'")
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         # Migration from 2.2: warehouse is no more required
         table.not_null_action('warehouse', 'remove')
 
@@ -212,72 +220,79 @@
         table.not_null_action('purchase_date', 'remove')
 
         # Add index on create_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action('create_date', action='add')
 
-    def default_payment_term(self):
-        payment_term_obj = Pool().get('account.invoice.payment_term')
-        payment_term_ids = payment_term_obj.search(self.payment_term.domain)
-        if len(payment_term_ids) == 1:
-            return payment_term_ids[0]
+    @classmethod
+    def default_payment_term(cls):
+        PaymentTerm = Pool().get('account.invoice.payment_term')
+        payment_terms = PaymentTerm.search(cls.payment_term.domain)
+        if len(payment_terms) == 1:
+            return payment_terms[0].id
 
-    def default_warehouse(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(self.warehouse.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_warehouse(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.warehouse.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_currency(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency():
+        Company = Pool().get('company.company')
         company = Transaction().context.get('company')
         if company:
-            company = company_obj.browse(company)
+            company = Company(company)
             return company.currency.id
 
-    def default_currency_digits(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency_digits():
+        Company = Pool().get('company.company')
         company = Transaction().context.get('company')
         if company:
-            company = company_obj.browse(company)
+            company = Company(company)
             return company.currency.digits
         return 2
 
-    def default_invoice_method(self):
-        configuration_obj = Pool().get('purchase.configuration')
-        configuration = configuration_obj.browse(1)
+    @staticmethod
+    def default_invoice_method():
+        Configuration = Pool().get('purchase.configuration')
+        configuration = Configuration(1)
         return configuration.purchase_invoice_method
 
-    def default_invoice_state(self):
+    @staticmethod
+    def default_invoice_state():
         return 'none'
 
-    def default_shipment_state(self):
+    @staticmethod
+    def default_shipment_state():
         return 'none'
 
-    def on_change_party(self, vals):
+    def on_change_party(self):
         pool = Pool()
-        party_obj = pool.get('party.party')
-        address_obj = pool.get('party.address')
-        payment_term_obj = pool.get('account.invoice.payment_term')
-        currency_obj = pool.get('currency.currency')
+        PaymentTerm = pool.get('account.invoice.payment_term')
+        Currency = pool.get('currency.currency')
         cursor = Transaction().cursor
-        res = {
+        changes = {
             'invoice_address': None,
             'payment_term': None,
             'currency': self.default_currency(),
             'currency_digits': self.default_currency_digits(),
-        }
-        if vals.get('party'):
-            party = party_obj.browse(vals['party'])
-            res['invoice_address'] = party_obj.address_get(party.id,
-                    type='invoice')
-            if party.supplier_payment_term:
-                res['payment_term'] = party.supplier_payment_term.id
+            }
+        invoice_address = None
+        payment_term = None
+        if self.party:
+            invoice_address = self.party.address_get(type='invoice')
+            if self.party.supplier_payment_term:
+                payment_term = self.party.supplier_payment_term.id
 
             subquery = cursor.limit_clause('SELECT currency '
                 'FROM "' + self._table + '" '
@@ -285,223 +300,139 @@
                 'ORDER BY id DESC', 10)
             cursor.execute('SELECT currency FROM (' + subquery + ') AS p '
                 'GROUP BY currency '
-                'ORDER BY COUNT(1) DESC', (party.id,))
+                'ORDER BY COUNT(1) DESC', (self.party.id,))
             row = cursor.fetchone()
             if row:
                 currency_id, = row
-                currency = currency_obj.browse(currency_id)
-                res['currency'] = currency.id
-                res['currency_digits'] = currency.digits
+                currency = Currency(currency_id)
+                changes['currency'] = currency.id
+                changes['currency_digits'] = currency.digits
 
-        if res['invoice_address']:
-            res['invoice_address.rec_name'] = address_obj.browse(
-                    res['invoice_address']).rec_name
-        if not res['payment_term']:
-            res['payment_term'] = self.default_payment_term()
-        if res['payment_term']:
-            res['payment_term.rec_name'] = payment_term_obj.browse(
-                    res['payment_term']).rec_name
-        return res
+        if invoice_address:
+            changes['invoice_address'] = invoice_address.id
+            changes['invoice_address.rec_name'] = invoice_address.rec_name
+        else:
+            changes['invoice_address'] = None
+        if payment_term:
+            changes['payment_term'] = payment_term.id
+            changes['payment_term.rec_name'] = payment_term.rec_name
+        else:
+            changes['payment_term'] = self.default_payment_term()
+            if changes['payment_term']:
+                changes['payment_term.rec_name'] = PaymentTerm(
+                    changes['payment_term']).rec_name
+        return changes
 
-    def on_change_with_currency_digits(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-            return currency.digits
+    def on_change_with_currency_digits(self, name=None):
+        if self.currency:
+            return self.currency.digits
         return 2
 
-    def get_currency_digits(self, purchases):
-        '''
-        Return the number of digits of the currency for each purchases
-
-        :param purchases: a BrowseRecordList of purchases
-        :return: a dictionary with purchase id as key and
-            number of digits as value
-        '''
-        res = {}
-        for purchase in purchases:
-            res[purchase.id] = purchase.currency.digits
-        return res
-
-    def on_change_with_party_lang(self, vals):
-        party_obj = Pool().get('party.party')
-        if vals.get('party'):
-            party = party_obj.browse(vals['party'])
-            if party.lang:
-                return party.lang.code
+    def on_change_with_party_lang(self, name=None):
+        if self.party:
+            if self.party.lang:
+                return self.party.lang.code
         return CONFIG['language']
 
-    def get_tax_context(self, purchase):
-        party_obj = Pool().get('party.party')
-        res = {}
-        if isinstance(purchase, dict):
-            if purchase.get('party'):
-                party = party_obj.browse(purchase['party'])
-                if party.lang:
-                    res['language'] = party.lang.code
-        else:
-            if purchase.party.lang:
-                res['language'] = purchase.party.lang.code
-        return res
+    def get_tax_context(self):
+        context = {}
+        if self.party and self.party.lang:
+            context['language'] = self.party.lang.code
+        return context
 
-    def on_change_lines(self, vals):
+    def on_change_lines(self):
         pool = Pool()
-        currency_obj = pool.get('currency.currency')
-        tax_obj = pool.get('account.tax')
-        invoice_obj = pool.get('account.invoice')
+        Tax = pool.get('account.tax')
+        Invoice = pool.get('account.invoice')
 
-        res = {
+        changes = {
             'untaxed_amount': Decimal('0.0'),
             'tax_amount': Decimal('0.0'),
             'total_amount': Decimal('0.0'),
-        }
-        currency = None
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-        if vals.get('lines'):
-            context = self.get_tax_context(vals)
+            }
+        if self.lines:
+            context = self.get_tax_context()
             taxes = {}
-            for line in vals['lines']:
-                if line.get('type', 'line') != 'line':
+            for line in self.lines:
+                if getattr(line, 'type', 'line') != 'line':
                     continue
-                res['untaxed_amount'] += line.get('amount') or Decimal(0)
+                changes['untaxed_amount'] += line.amount or Decimal(0)
 
                 with Transaction().set_context(context):
-                    tax_list = tax_obj.compute(line.get('taxes', []),
-                            line.get('unit_price') or Decimal('0.0'),
-                            line.get('quantity') or 0.0)
+                    tax_list = Tax.compute(getattr(line, 'taxes', []),
+                        line.unit_price or Decimal('0.0'),
+                        line.quantity or 0.0)
                 for tax in tax_list:
-                    key, val = invoice_obj._compute_tax(tax, 'in_invoice')
+                    key, val = Invoice._compute_tax(tax, 'in_invoice')
                     if not key in taxes:
                         taxes[key] = val['amount']
                     else:
                         taxes[key] += val['amount']
-            if currency:
+            if self.currency:
                 for value in taxes.itervalues():
-                    res['tax_amount'] += currency_obj.round(currency, value)
-        if currency:
-            res['untaxed_amount'] = currency_obj.round(currency,
-                    res['untaxed_amount'])
-            res['tax_amount'] = currency_obj.round(currency, res['tax_amount'])
-        res['total_amount'] = res['untaxed_amount'] + res['tax_amount']
-        if currency:
-            res['total_amount'] = currency_obj.round(currency,
-                    res['total_amount'])
-        return res
+                    changes['tax_amount'] += self.currency.round(value)
+        if self.currency:
+            changes['untaxed_amount'] = self.currency.round(
+                changes['untaxed_amount'])
+            changes['tax_amount'] = self.currency.round(changes['tax_amount'])
+        changes['total_amount'] = (changes['untaxed_amount']
+            + changes['tax_amount'])
+        if self.currency:
+            changes['total_amount'] = self.currency.round(
+                changes['total_amount'])
+        return changes
 
-    def get_function_fields(self, ids, names):
-        '''
-        Function to compute function fields for purchase ids.
-
-        :param ids: the ids of the purchases
-        :param names: the list of field name to compute
-        :param args: optional argument
-        :return: a dictionary with all field names as key and
-            a dictionary as value with id as key
-        '''
-        res = {}
-        purchases = self.browse(ids)
-        if 'currency_digits' in names:
-            res['currency_digits'] = self.get_currency_digits(purchases)
-        if 'party_lang' in names:
-            res['party_lang'] = self.get_party_lang(purchases)
-        if 'shipments' in names:
-            res['shipments'] = self.get_shipments(purchases)
-        if 'moves' in names:
-            res['moves'] = self.get_moves(purchases)
-        return res
-
-    def get_party_lang(self, purchases):
-        '''
-        Return the language code of the party of each purchases
-
-        :param purchases: a BrowseRecordList of purchases
-        :return: a dictionary with purchase id as key and
-            a language code as value
-        '''
-        res = {}
-        for purchase in purchases:
-            if purchase.party.lang:
-                res[purchase.id] = purchase.party.lang.code
-            else:
-                res[purchase.id] = CONFIG['language']
-        return res
-
-    def get_untaxed_amount(self, ids, name):
+    def get_untaxed_amount(self, name):
         '''
         Return the untaxed amount for each purchases
         '''
-        currency_obj = Pool().get('currency.currency')
-        amounts = {}
-        for purchase in self.browse(ids):
-            if (purchase.state in self._states_cached
-                    and purchase.untaxed_amount_cache is not None):
-                amounts[purchase.id] = purchase.untaxed_amount_cache
+        if (self.state in self._states_cached
+                and self.untaxed_amount_cache is not None):
+            return self.untaxed_amount_cache
+        amount = sum((l.amount for l in self.lines
+                if l.type == 'line'), Decimal(0))
+        return self.currency.round(amount)
+
+    def get_tax_amount(self, name):
+        pool = Pool()
+        Tax = pool.get('account.tax')
+        Invoice = pool.get('account.invoice')
+
+        if (self.state in self._states_cached
+                and self.tax_amount_cache is not None):
+            return self.tax_amount_cache
+        context = self.get_tax_context()
+        taxes = {}
+        for line in self.lines:
+            if line.type != 'line':
                 continue
-            amount = sum((l.amount for l in purchase.lines
-                    if l.type == 'line'), Decimal(0))
-            amounts[purchase.id] = currency_obj.round(purchase.currency,
-                    amount)
-        return amounts
+            with Transaction().set_context(context):
+                tax_list = Tax.compute(line.taxes, line.unit_price,
+                    line.quantity)
+            # Don't round on each line to handle rounding error
+            for tax in tax_list:
+                key, val = Invoice._compute_tax(tax, 'in_invoice')
+                if not key in taxes:
+                    taxes[key] = val['amount']
+                else:
+                    taxes[key] += val['amount']
+        amount = sum((self.currency.round(tax) for tax in taxes.itervalues()),
+            Decimal(0))
+        return self.currency.round(amount)
 
-    def get_tax_amount(self, ids, name):
-        '''
-        Return the tax amount for each purchases
-        '''
-        pool = Pool()
-        currency_obj = pool.get('currency.currency')
-        tax_obj = pool.get('account.tax')
-        invoice_obj = pool.get('account.invoice')
+    def get_total_amount(self, name):
+        if (self.state in self._states_cached
+                and self.total_amount_cache is not None):
+            return self.total_amount_cache
+        return self.currency.round(self.untaxed_amount + self.tax_amount)
 
-        amounts = {}
-        for purchase in self.browse(ids):
-            if (purchase.state in self._states_cached
-                    and purchase.tax_amount_cache is not None):
-                amounts[purchase.id] = purchase.tax_amount_cache
-                continue
-            context = self.get_tax_context(purchase)
-            taxes = {}
-            for line in purchase.lines:
-                if line.type != 'line':
-                    continue
-                with Transaction().set_context(context):
-                    tax_list = tax_obj.compute([t.id for t in line.taxes],
-                            line.unit_price, line.quantity)
-                # Don't round on each line to handle rounding error
-                for tax in tax_list:
-                    key, val = invoice_obj._compute_tax(tax, 'in_invoice')
-                    if not key in taxes:
-                        taxes[key] = val['amount']
-                    else:
-                        taxes[key] += val['amount']
-            amount = sum((currency_obj.round(purchase.currency, tax)
-                    for tax in taxes.itervalues()), Decimal(0))
-            amounts[purchase.id] = currency_obj.round(purchase.currency,
-                amount)
-        return amounts
-
-    def get_total_amount(self, ids, name):
-        '''
-        Return the total amount of each purchases
-        '''
-        currency_obj = Pool().get('currency.currency')
-        amounts = {}
-        for purchase in self.browse(ids):
-            if (purchase.state in self._states_cached
-                    and purchase.total_amount_cache is not None):
-                amounts[purchase.id] = purchase.total_amount_cache
-                continue
-            amounts[purchase.id] = currency_obj.round(purchase.currency,
-                    purchase.untaxed_amount + purchase.tax_amount)
-        return amounts
-
-    def get_invoice_state(self, purchase):
+    def get_invoice_state(self):
         '''
         Return the invoice state for the purchase.
         '''
-        skip_ids = set(x.id for x in purchase.invoices_ignored)
-        skip_ids.update(x.id for x in purchase.invoices_recreated)
-        invoices = [i for i in purchase.invoices if i.id not in skip_ids]
+        skip_ids = set(x.id for x in self.invoices_ignored)
+        skip_ids.update(x.id for x in self.invoices_recreated)
+        invoices = [i for i in self.invoices if i.id not in skip_ids]
         if invoices:
             if any(i.state == 'cancel' for i in invoices):
                 return 'exception'
@@ -511,93 +442,69 @@
                 return 'waiting'
         return 'none'
 
-    def set_invoice_state(self, purchase):
+    def set_invoice_state(self):
         '''
         Set the invoice state.
         '''
-        state = self.get_invoice_state(purchase)
-        if purchase.invoice_state != state:
-            self.write(purchase.id, {
+        state = self.get_invoice_state()
+        if self.invoice_state != state:
+            self.write([self], {
                     'invoice_state': state,
                     })
 
-    def get_shipments(self, purchases):
-        '''
-        Return the shipments for the purchases.
+    def get_shipments(self, name):
+        shipments = []
+        for line in self.lines:
+            for move in line.moves:
+                if move.shipment_in:
+                    if move.shipment_in.id not in shipments:
+                        shipments.append(move.shipment_in.id)
+        return shipments
 
-        :param purchases: a BrowseRecordList of purchases
-        :return: a dictionary with purchase id as key and
-            a list of shipment_in id as value
-        '''
-        res = {}
-        for purchase in purchases:
-            res[purchase.id] = []
-            for line in purchase.lines:
-                for move in line.moves:
-                    if move.shipment_in:
-                        if move.shipment_in.id not in res[purchase.id]:
-                            res[purchase.id].append(move.shipment_in.id)
-        return res
+    def get_moves(self, name):
+        return [m.id for l in self.lines for m in l.moves]
 
-    def get_moves(self, purchases):
-        '''
-        Return the moves for the purchases.
-
-        :param purchases: a BrowseRecordList of purchases
-        :return: a dictionary with purchase id as key and
-            a list of moves id as value
-        '''
-        res = {}
-        for purchase in purchases:
-            res[purchase.id] = []
-            for line in purchase.lines:
-                res[purchase.id].extend([x.id for x in line.moves])
-        return res
-
-    def get_shipment_state(self, purchase):
+    def get_shipment_state(self):
         '''
         Return the shipment state for the purchase.
         '''
-        if purchase.moves:
-            if any(l.move_exception for l in purchase.lines):
+        if self.moves:
+            if any(l.move_exception for l in self.lines):
                 return 'exception'
-            elif all(l.move_done for l in purchase.lines):
+            elif all(l.move_done for l in self.lines):
                 return 'received'
             else:
                 return 'waiting'
         return 'none'
 
-    def set_shipment_state(self, purchase):
+    def set_shipment_state(self):
         '''
         Set the shipment state.
         '''
-        state = self.get_shipment_state(purchase)
-        if purchase.shipment_state != state:
-            self.write(purchase.id, {
+        state = self.get_shipment_state()
+        if self.shipment_state != state:
+            self.write([self], {
                     'shipment_state': state,
                     })
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        for purchase in self.browse(ids):
-            res[purchase.id] = purchase.reference or str(purchase.id) \
-                    + ' - ' + purchase.party.name
-        return res
+    def get_rec_name(self, name):
+        return (self.reference or str(self.id)
+            + ' - ' + self.party.name)
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         names = clause[2].split(' - ', 1)
-        ids = self.search(['OR',
-            ('reference', clause[1], names[0]),
-            ('supplier_reference', clause[1], names[0]),
-            ], order=[])
-        res = [('id', 'in', ids)]
+        purchases = cls.search(['OR',
+                ('reference', clause[1], names[0]),
+                ('supplier_reference', clause[1], names[0]),
+                ], order=[])
+        res = [('id', 'in', [p.id for p in purchases])]
         if len(names) != 1 and names[1]:
             res.append(('party', clause[1], names[1]))
         return res
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, purchases, default=None):
         if default is None:
             default = {}
         default = default.copy()
@@ -608,260 +515,244 @@
         default['invoices_ignored'] = None
         default['shipment_state'] = 'none'
         default.setdefault('purchase_date', None)
-        return super(Purchase, self).copy(ids, default=default)
+        return super(Purchase, cls).copy(purchases, default=default)
 
-    def check_for_quotation(self, ids):
-        purchases = self.browse(ids)
-        for purchase in purchases:
-            if not purchase.invoice_address:
-                self.raise_user_error('invoice_addresse_required')
-            for line in purchase.lines:
-                if (not line.to_location
-                        and line.product
-                        and line.product.type in ('goods', 'assets')):
-                    self.raise_user_error('warehouse_required')
+    def check_for_quotation(self):
+        if not self.invoice_address:
+            self.raise_user_error('invoice_addresse_required')
+        for line in self.lines:
+            if (not line.to_location
+                    and line.product
+                    and line.product.type in ('goods', 'assets')):
+                self.raise_user_error('warehouse_required')
 
-    def set_reference(self, ids):
+    @classmethod
+    def set_reference(cls, purchases):
         '''
         Fill the reference field with the purchase sequence
         '''
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('purchase.configuration')
+        pool = Pool()
+        Sequence = pool.get('ir.sequence')
+        Config = pool.get('purchase.configuration')
 
-        config = config_obj.browse(1)
-        purchases = self.browse(ids)
+        config = Config(1)
         for purchase in purchases:
             if purchase.reference:
                 continue
-            reference = sequence_obj.get_id(config.purchase_sequence.id)
-            self.write(purchase.id, {
-                'reference': reference,
-                })
-
-    def set_purchase_date(self, ids):
-        date_obj = Pool().get('ir.date')
-        for purchase in self.browse(ids):
-            if not purchase.purchase_date:
-                self.write(purchase.id, {
-                    'purchase_date': date_obj.today(),
+            reference = Sequence.get_id(config.purchase_sequence.id)
+            cls.write([purchase], {
+                    'reference': reference,
                     })
 
-    def store_cache(self, ids):
-        for purchase in self.browse(ids):
-            self.write(purchase.id, {
+    @classmethod
+    def set_purchase_date(cls, purchases):
+        Date = Pool().get('ir.date')
+        for purchase in purchases:
+            if not purchase.purchase_date:
+                cls.write([purchase], {
+                        'purchase_date': Date.today(),
+                        })
+
+    @classmethod
+    def store_cache(cls, purchases):
+        for purchase in purchases:
+            cls.write([purchase], {
                     'untaxed_amount_cache': purchase.untaxed_amount,
                     'tax_amount_cache': purchase.tax_amount,
                     'total_amount_cache': purchase.total_amount,
                     })
 
-    def _get_invoice_line_purchase_line(self, purchase):
+    def _get_invoice_line_purchase_line(self):
         '''
-        Return invoice line values for each purchase lines
-
-        :param purchase: a BrowseRecord of the purchase
-        :return: a dictionary with invoiced purchase line id as key
-            and a list of invoice line values as value
+        Return invoice line for each purchase lines
         '''
-        line_obj = Pool().get('purchase.line')
         res = {}
-        for line in purchase.lines:
-            val = line_obj.get_invoice_line(line)
+        for line in self.lines:
+            val = line.get_invoice_line()
             if val:
                 res[line.id] = val
         return res
 
-    def _get_invoice_purchase(self, purchase):
+    def _get_invoice_purchase(self):
         '''
-        Return invoice values for purchase
-
-        :param purchase: the BrowseRecord of the purchase
-
-        :return: a dictionary with purchase fields as key and
-            purchase values as value
-        '''
-        journal_obj = Pool().get('account.journal')
-
-        journal_id = journal_obj.search([
-            ('type', '=', 'expense'),
-            ], limit=1)
-        if journal_id:
-            journal_id = journal_id[0]
-
-        res = {
-            'company': purchase.company.id,
-            'type': 'in_invoice',
-            'reference': purchase.reference,
-            'journal': journal_id,
-            'party': purchase.party.id,
-            'invoice_address': purchase.invoice_address.id,
-            'currency': purchase.currency.id,
-            'account': purchase.party.account_payable.id,
-            'payment_term': purchase.payment_term.id,
-        }
-        return res
-
-    def create_invoice(self, purchase):
-        '''
-        Create an invoice for the purchase and return the id
+        Return invoice
         '''
         pool = Pool()
-        invoice_obj = pool.get('account.invoice')
-        invoice_line_obj = pool.get('account.invoice.line')
-        purchase_line_obj = pool.get('purchase.line')
+        Journal = pool.get('account.journal')
+        Invoice = pool.get('account.invoice')
 
-        if purchase.invoice_method == 'manual':
+        journals = Journal.search([
+                ('type', '=', 'expense'),
+                ], limit=1)
+        if journals:
+            journal, = journals
+        else:
+            journal = None
+
+        with Transaction().set_user(0, set_context=True):
+            return Invoice(
+                company=self.company,
+                type='in_invoice',
+                reference=self.reference,
+                journal=journal,
+                party=self.party,
+                invoice_address=self.invoice_address,
+                currency=self.currency,
+                account=self.party.account_payable,
+                payment_term=self.payment_term,
+                )
+
+    def create_invoice(self):
+        '''
+        Create an invoice for the purchase and return it
+        '''
+        pool = Pool()
+        Invoice = pool.get('account.invoice')
+        PurchaseLine = pool.get('purchase.line')
+
+        if self.invoice_method == 'manual':
             return
 
-        if not purchase.party.account_payable:
+        if not self.party.account_payable:
             self.raise_user_error('missing_account_payable',
-                    error_args=(purchase.party.rec_name,))
+                    error_args=(self.party.rec_name,))
 
-        invoice_lines = self._get_invoice_line_purchase_line(purchase)
+        invoice_lines = self._get_invoice_line_purchase_line()
         if not invoice_lines:
             return
 
-        vals = self._get_invoice_purchase(purchase)
-        with Transaction().set_user(0, set_context=True):
-            invoice_id = invoice_obj.create(vals)
+        invoice = self._get_invoice_purchase()
+        invoice.save()
 
-        for line in purchase.lines:
+        for line in self.lines:
             if line.id not in invoice_lines:
                 continue
-            for vals in invoice_lines[line.id]:
-                vals['invoice'] = invoice_id
-                with Transaction().set_user(0, set_context=True):
-                    invoice_line_id = invoice_line_obj.create(vals)
-                purchase_line_obj.write(line.id, {
-                    'invoice_lines': [('add', invoice_line_id)],
-                    })
+            for invoice_line in invoice_lines[line.id]:
+                invoice_line.invoice = invoice.id
+                invoice_line.save()
+                PurchaseLine.write([line], {
+                        'invoice_lines': [('add', [invoice_line.id])],
+                        })
 
         with Transaction().set_user(0, set_context=True):
-            invoice_obj.update_taxes([invoice_id])
+            Invoice.update_taxes([invoice])
 
-        self.write(purchase.id, {
-            'invoices': [('add', invoice_id)],
-        })
-        return invoice_id
+        self.write([self], {
+                'invoices': [('add', [invoice.id])],
+                })
+        return invoice
 
-    def create_move(self, purchase):
+    def create_move(self):
         '''
         Create move for each purchase lines
         '''
-        line_obj = Pool().get('purchase.line')
+        for line in self.lines:
+            line.create_move()
 
-        for line in purchase.lines:
-            line_obj.create_move(line)
+    def is_done(self):
+        return ((self.invoice_state == 'paid'
+                or self.invoice_method == 'manual')
+            and self.shipment_state == 'received')
 
-    def is_done(self, purchase):
-        return ((purchase.invoice_state == 'paid'
-                or purchase.invoice_method == 'manual')
-            and purchase.shipment_state == 'received')
+    @classmethod
+    def delete(cls, purchases):
+        # Cancel before delete
+        cls.cancel(purchases)
+        for purchase in purchases:
+            if purchase.state != 'cancel':
+                cls.raise_user_error('delete_cancel', purchase.rec_name)
+        super(Purchase, cls).delete(purchases)
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        # Cancel before delete
-        self.cancel(ids)
-        for purchase in self.browse(ids):
-            if purchase.state != 'cancel':
-                self.raise_user_error('delete_cancel', purchase.rec_name)
-        return super(Purchase, self).delete(ids)
-
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        self.store_cache(ids)
+    def cancel(cls, purchases):
+        cls.store_cache(purchases)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
+    def draft(cls, purchases):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('quotation')
-    def quote(self, ids):
-        self.check_for_quotation(ids)
-        self.set_reference(ids)
+    def quote(cls, purchases):
+        for purchase in purchases:
+            purchase.check_for_quotation()
+        cls.set_reference(purchases)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('confirmed')
-    def confirm(self, ids):
-        self.set_purchase_date(ids)
-        self.store_cache(ids)
-        self.process(ids)
+    def confirm(cls, purchases):
+        cls.set_purchase_date(purchases)
+        cls.store_cache(purchases)
+        cls.process(purchases)
 
+    @classmethod
     @ModelView.button_action('purchase.wizard_invoice_handle_exception')
-    def handle_invoice_exception(self, ids):
+    def handle_invoice_exception(cls, purchases):
         pass
 
+    @classmethod
     @ModelView.button_action('purchase.wizard_shipment_handle_exception')
-    def handle_shipment_exception(self, ids):
+    def handle_shipment_exception(cls, purchases):
         pass
 
-    def process(self, ids):
+    @classmethod
+    def process(cls, purchases):
         done = []
-        for purchase in self.browse(ids):
+        for purchase in purchases:
             if purchase.state in ('done', 'cancel'):
                 continue
-            self.create_invoice(purchase)
-            self.set_invoice_state(purchase)
-            self.create_move(purchase)
-            self.set_shipment_state(purchase)
-            if self.is_done(purchase):
-                done.append(purchase.id)
+            purchase.create_invoice()
+            purchase.set_invoice_state()
+            purchase.create_move()
+            purchase.set_shipment_state()
+            if purchase.is_done():
+                done.append(purchase)
         if done:
-            self.write(done, {
+            cls.write(done, {
                     'state': 'done',
                     })
 
-Purchase()
-
 
 class PurchaseInvoice(ModelSQL):
     'Purchase - Invoice'
-    _name = 'purchase.purchase-account.invoice'
+    __name__ = 'purchase.purchase-account.invoice'
     _table = 'purchase_invoices_rel'
-    _description = __doc__
     purchase = fields.Many2One('purchase.purchase', 'Purchase',
             ondelete='CASCADE', select=True, required=True)
     invoice = fields.Many2One('account.invoice', 'Invoice',
             ondelete='RESTRICT', select=True, required=True)
 
-PurchaseInvoice()
 
-
-class PuchaseIgnoredInvoice(ModelSQL):
+class PurchaseIgnoredInvoice(ModelSQL):
     'Purchase - Ignored Invoice'
-    _name = 'purchase.purchase-ignored-account.invoice'
+    __name__ = 'purchase.purchase-ignored-account.invoice'
     _table = 'purchase_invoice_ignored_rel'
-    _description = __doc__
     purchase = fields.Many2One('purchase.purchase', 'Purchase',
             ondelete='CASCADE', select=True, required=True)
     invoice = fields.Many2One('account.invoice', 'Invoice',
             ondelete='RESTRICT', select=True, required=True)
 
-PuchaseIgnoredInvoice()
-
 
 class PurchaseRecreadtedInvoice(ModelSQL):
     'Purchase - Recreated Invoice'
-    _name = 'purchase.purchase-recreated-account.invoice'
+    __name__ = 'purchase.purchase-recreated-account.invoice'
     _table = 'purchase_invoice_recreated_rel'
-    _description = __doc__
     purchase = fields.Many2One('purchase.purchase', 'Purchase',
             ondelete='CASCADE', select=True, required=True)
     invoice = fields.Many2One('account.invoice', 'Invoice',
             ondelete='RESTRICT', select=True, required=True)
 
-PurchaseRecreadtedInvoice()
-
 
 class PurchaseLine(ModelSQL, ModelView):
     'Purchase Line'
-    _name = 'purchase.line'
+    __name__ = 'purchase.line'
     _rec_name = 'description'
-    _description = __doc__
-
     purchase = fields.Many2One('purchase.purchase', 'Purchase',
             ondelete='CASCADE', select=True, required=True)
     sequence = fields.Integer('Sequence',
@@ -898,7 +789,7 @@
             '_parent_purchase.party'],
         depends=['product', 'type', 'product_uom_category'])
     unit_digits = fields.Function(fields.Integer('Unit Digits',
-        on_change_with=['unit']), 'get_unit_digits')
+            on_change_with=['unit']), 'on_change_with_unit_digits')
     product = fields.Many2One('product.product', 'Product',
         domain=[('purchasable', '=', True)],
         states={
@@ -920,7 +811,7 @@
     product_uom_category = fields.Function(
         fields.Many2One('product.uom.category', 'Product Uom Category',
             on_change_with=['product']),
-        'get_product_uom_category')
+        'on_change_with_product_uom_category')
     unit_price = fields.Numeric('Unit Price', digits=(16, 4),
         states={
             'invisible': Eval('type') != 'line',
@@ -960,12 +851,13 @@
     delivery_date = fields.Function(fields.Date('Delivery Date',
             on_change_with=['product', '_parent_purchase.purchase_date',
                 '_parent_purchase.party']),
-        'get_delivery_date')
+        'on_change_with_delivery_date')
 
-    def __init__(self):
-        super(PurchaseLine, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(PurchaseLine, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._error_messages.update({
             'supplier_location_required': 'The supplier location is required!',
             'missing_account_expense': 'It misses ' \
                     'an "Account Expense" on product "%s"!',
@@ -973,345 +865,261 @@
                     'an "account expense" default property!',
             })
 
-    def init(self, module_name):
-        super(PurchaseLine, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(PurchaseLine, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 comment change into note
         if table.column_exist('comment'):
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET note = comment')
             table.drop_column('comment', exception=True)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'line'
 
-    def get_move_done(self, ids, name):
-        uom_obj = Pool().get('product.uom')
-        res = {}
-        for line in self.browse(ids):
-            val = True
-            if not line.product:
-                res[line.id] = True
-                continue
-            if line.product.type == 'service':
-                res[line.id] = True
-                continue
-            skip_ids = set(x.id for x in line.moves_recreated + \
-                                line.moves_ignored)
-            quantity = line.quantity
-            for move in line.moves:
-                if move.state != 'done' \
-                        and move.id not in skip_ids:
-                    val = False
-                    break
-                quantity -= uom_obj.compute_qty(move.uom, move.quantity,
-                        line.unit)
-            if val:
-                if quantity > 0.0:
-                    val = False
-            res[line.id] = val
-        return res
+    def get_move_done(self, name):
+        Uom = Pool().get('product.uom')
+        done = True
+        if not self.product:
+            return True
+        if self.product.type == 'service':
+            return True
+        skip_ids = set(x.id for x in self.moves_recreated
+            + self.moves_ignored)
+        quantity = self.quantity
+        for move in self.moves:
+            if move.state != 'done' \
+                    and move.id not in skip_ids:
+                done = False
+                break
+            quantity -= Uom.compute_qty(move.uom, move.quantity, self.unit)
+        if done:
+            if quantity > 0.0:
+                done = False
+        return done
 
-    def get_move_exception(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            val = False
-            skip_ids = set(x.id for x in line.moves_ignored + \
-                               line.moves_recreated)
-            for move in line.moves:
-                if move.state == 'cancel' \
-                        and move.id not in skip_ids:
-                    val = True
-                    break
-            res[line.id] = val
-        return res
+    def get_move_exception(self, name):
+        skip_ids = set(x.id for x in self.moves_ignored
+            + self.moves_recreated)
+        for move in self.moves:
+            if move.state == 'cancel' \
+                    and move.id not in skip_ids:
+                return True
+        return False
 
-    def on_change_with_unit_digits(self, vals):
-        uom_obj = Pool().get('product.uom')
-        if vals.get('unit'):
-            uom = uom_obj.browse(vals['unit'])
-            return uom.digits
+    def on_change_with_unit_digits(self, name=None):
+        if self.unit:
+            return self.unit.digits
         return 2
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            if line.unit:
-                res[line.id] = line.unit.digits
-            else:
-                res[line.id] = 2
-        return res
-
-    def _get_tax_rule_pattern(self, party, vals):
+    def _get_tax_rule_pattern(self):
         '''
         Get tax rule pattern
+        '''
+        return {}
 
-        :param party: the BrowseRecord of the party
-        :param vals: a dictionary with value from on_change
-        :return: a dictionary to use as pattern for tax rule
-        '''
-        res = {}
-        return res
+    def _get_context_purchase_price(self):
+        context = {}
+        if self.purchase:
+            if self.purchase.currency:
+                context['currency'] = self.purchase.currency.id
+            if self.purchase.party:
+                context['supplier'] = self.purchase.party.id
+            if self.purchase.purchase_date:
+                context['purchase_date'] = self.purchase.purchase_date
+        if self.unit:
+            context['uom'] = self.unit.id
+        else:
+            self.product.purchase_uom.id
+        return context
 
-    def on_change_product(self, vals):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        product_obj = pool.get('product.product')
-        tax_rule_obj = pool.get('account.tax.rule')
+    def on_change_product(self):
+        Product = Pool().get('product.product')
 
-        if not vals.get('product'):
+        if not self.product:
             return {}
         res = {}
 
         context = {}
         party = None
-        if vals.get('_parent_purchase.party'):
-            party = party_obj.browse(vals['_parent_purchase.party'])
+        if self.purchase and self.purchase.party:
+            party = self.purchase.party
             if party.lang:
                 context['language'] = party.lang.code
 
-        product = product_obj.browse(vals['product'])
-
-        context2 = {}
-        if vals.get('_parent_purchase.currency'):
-            context2['currency'] = vals['_parent_purchase.currency']
-        if vals.get('_parent_purchase.party'):
-            context2['supplier'] = vals['_parent_purchase.party']
-        if vals.get('unit'):
-            context2['uom'] = vals['unit']
-        else:
-            context2['uom'] = product.purchase_uom.id
-        if vals.get('_parent_purchase.purchase_date'):
-            context2['purchase_date'] = vals['_parent_purchase.purchase_date']
-        with Transaction().set_context(context2):
-            res['unit_price'] = product_obj.get_purchase_price([product.id],
-                    vals.get('quantity') or 0)[product.id]
+        with Transaction().set_context(self._get_context_purchase_price()):
+            res['unit_price'] = Product.get_purchase_price([self.product],
+                self.quantity or 0)[self.product.id]
             if res['unit_price']:
                 res['unit_price'] = res['unit_price'].quantize(
-                    Decimal(1) / 10 ** self.unit_price.digits[1])
+                    Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
         res['taxes'] = []
-        pattern = self._get_tax_rule_pattern(party, vals)
-        for tax in product.supplier_taxes_used:
+        pattern = self._get_tax_rule_pattern()
+        for tax in self.product.supplier_taxes_used:
             if party and party.supplier_tax_rule:
-                tax_ids = tax_rule_obj.apply(party.supplier_tax_rule, tax,
-                        pattern)
+                tax_ids = party.supplier_tax_rule.apply(tax, pattern)
                 if tax_ids:
                     res['taxes'].extend(tax_ids)
                 continue
             res['taxes'].append(tax.id)
         if party and party.supplier_tax_rule:
-            tax_ids = tax_rule_obj.apply(party.supplier_tax_rule, None,
-                    pattern)
+            tax_ids = party.supplier_tax_rule.apply(None, pattern)
             if tax_ids:
                 res['taxes'].extend(tax_ids)
 
-        if not vals.get('description'):
+        if not self.description:
             with Transaction().set_context(context):
-                res['description'] = product_obj.browse(product.id).rec_name
+                res['description'] = Product(self.product.id).rec_name
 
-        category = product.purchase_uom.category
-        if not vals.get('unit') \
-                or vals.get('unit') not in [x.id for x in category.uoms]:
-            res['unit'] = product.purchase_uom.id
-            res['unit.rec_name'] = product.purchase_uom.rec_name
-            res['unit_digits'] = product.purchase_uom.digits
+        category = self.product.purchase_uom.category
+        if not self.unit or self.unit not in category.uoms:
+            res['unit'] = self.product.purchase_uom.id
+            res['unit.rec_name'] = self.product.purchase_uom.rec_name
+            res['unit_digits'] = self.product.purchase_uom.digits
 
-        vals = vals.copy()
-        vals['unit_price'] = res['unit_price']
-        vals['type'] = 'line'
-        res['amount'] = self.on_change_with_amount(vals)
+        self.unit_price = res['unit_price']
+        self.type = 'line'
+        res['amount'] = self.on_change_with_amount()
         return res
 
-    def on_change_with_product_uom_category(self, values):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        if values.get('product'):
-            product = product_obj.browse(values['product'])
-            return product.default_uom_category.id
+    def on_change_with_product_uom_category(self, name=None):
+        if self.product:
+            return self.product.default_uom_category.id
 
-    def get_product_uom_category(self, ids, name):
-        categories = {}
-        for line in self.browse(ids):
-            if line.product:
-                categories[line.id] = line.product.default_uom_category.id
-            else:
-                categories[line.id] = None
-        return categories
+    def on_change_quantity(self):
+        Product = Pool().get('product.product')
 
-    def on_change_quantity(self, vals):
-        product_obj = Pool().get('product.product')
-
-        if not vals.get('product'):
+        if not self.product:
             return {}
         res = {}
 
-        context = {}
-        if vals.get('_parent_purchase.currency'):
-            context['currency'] = vals['_parent_purchase.currency']
-        if vals.get('_parent_purchase.party'):
-            context['supplier'] = vals['_parent_purchase.party']
-        if vals.get('_parent_purchase.purchase_date'):
-            context['purchase_date'] = vals['_parent_purchase.purchase_date']
-        if vals.get('unit'):
-            context['uom'] = vals['unit']
-        with Transaction().set_context(context):
-            res['unit_price'] = product_obj.get_purchase_price(
-                    [vals['product']], vals.get('quantity') or 0
-                    )[vals['product']]
+        with Transaction().set_context(self._get_context_purchase_price()):
+            res['unit_price'] = Product.get_purchase_price([self.product],
+                self.quantity or 0)[self.product.id]
             if res['unit_price']:
                 res['unit_price'] = res['unit_price'].quantize(
-                    Decimal(1) / 10 ** self.unit_price.digits[1])
+                    Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
         return res
 
-    def on_change_unit(self, vals):
-        return self.on_change_quantity(vals)
+    def on_change_unit(self):
+        return self.on_change_quantity()
 
-    def on_change_with_amount(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('type') == 'line':
-            currency = vals.get('_parent_purchase.currency')
-            if currency and isinstance(currency, (int, long)):
-                currency = currency_obj.browse(
-                        vals['_parent_purchase.currency'])
-            amount = Decimal(str(vals.get('quantity') or '0.0')) * \
-                    (vals.get('unit_price') or Decimal('0.0'))
+    def on_change_with_amount(self):
+        if self.type == 'line':
+            currency = self.purchase.currency if self.purchase else None
+            amount = Decimal(str(self.quantity or '0.0')) * \
+                (self.unit_price or Decimal('0.0'))
             if currency:
-                return currency_obj.round(currency, amount)
+                return currency.round(amount)
             return amount
         return Decimal('0.0')
 
-    def get_amount(self, ids, name):
-        currency_obj = Pool().get('currency.currency')
-        res = {}
-        for line in self.browse(ids):
-            if line.type == 'line':
-                res[line.id] = currency_obj.round(line.purchase.currency,
-                        Decimal(str(line.quantity)) * line.unit_price)
-            elif line.type == 'subtotal':
-                res[line.id] = Decimal('0.0')
-                for line2 in line.purchase.lines:
-                    if line2.type == 'line':
-                        res[line.id] += currency_obj.round(
-                            line2.purchase.currency,
-                            Decimal(str(line2.quantity)) * line2.unit_price)
-                    elif line2.type == 'subtotal':
-                        if line.id == line2.id:
-                            break
-                        res[line.id] = Decimal('0.0')
-            else:
-                res[line.id] = Decimal('0.0')
-        return res
+    def get_amount(self, name):
+        if self.type == 'line':
+            return self.purchase.currency.round(
+                Decimal(str(self.quantity)) * self.unit_price)
+        elif self.type == 'subtotal':
+            amount = Decimal('0.0')
+            for line2 in self.purchase.lines:
+                if line2.type == 'line':
+                    amount += line2.purchase.currency.round(
+                        Decimal(str(line2.quantity)) * line2.unit_price)
+                elif line2.type == 'subtotal':
+                    if self == line2:
+                        break
+                    amount = Decimal('0.0')
+            return amount
+        return Decimal('0.0')
 
-    def get_from_location(self, ids, name):
-        result = {}
-        for line in self.browse(ids):
-            result[line.id] = line.purchase.party.supplier_location.id
-        return result
+    def get_from_location(self, name):
+        return self.purchase.party.supplier_location.id
 
-    def get_to_location(self, ids, name):
-        result = {}
-        for line in self.browse(ids):
-            if line.purchase.warehouse:
-                result[line.id] = line.purchase.warehouse.input_location.id
-            else:
-                result[line.id] = None
-        return result
+    def get_to_location(self, name):
+        if self.purchase.warehouse:
+            return self.purchase.warehouse.input_location.id
 
-    def _compute_delivery_date(self, product, party, date):
-        product_supplier_obj = Pool().get('purchase.product_supplier')
-        if product and product.product_suppliers:
-            for product_supplier in product.product_suppliers:
-                if product_supplier.party.id == party.id:
-                    return product_supplier_obj.compute_supply_date(
-                        product_supplier, date=date)
+    def on_change_with_delivery_date(self, name=None):
+        if (self.product
+                and self.purchase and self.purchase.party
+                and self.product.product_suppliers):
+            date = self.purchase.purchase_date if self.purchase else None
+            for product_supplier in self.product.product_suppliers:
+                if product_supplier.party == self.purchase.party:
+                    return product_supplier.compute_supply_date(date=date)
 
-    def on_change_with_delivery_date(self, values):
+    def get_invoice_line(self):
+        '''
+        Return a list of invoice line for purchase line
+        '''
         pool = Pool()
-        product_obj = pool.get('product.product')
-        party_obj = pool.get('party.party')
-        if values.get('product') and values.get('_parent_purchase.party'):
-            product = product_obj.browse(values['product'])
-            party = party_obj.browse(values['_parent_purchase.party'])
-            return self._compute_delivery_date(product, party,
-                values.get('_parent_purchase.purchase_date'))
+        Uom = pool.get('product.uom')
+        Property = pool.get('ir.property')
+        InvoiceLine = pool.get('account.invoice.line')
 
-    def get_delivery_date(self, ids, name):
-        dates = {}
-        for line in self.browse(ids):
-            dates[line.id] = self._compute_delivery_date(line.product,
-                line.purchase.party, line.purchase.purchase_date)
-        return dates
-
-    def get_invoice_line(self, line):
-        '''
-        Return invoice line values for purchase line
-
-        :param line: a BrowseRecord of the purchase line
-        :return: a list of invoice line values
-        '''
-        uom_obj = Pool().get('product.uom')
-        property_obj = Pool().get('ir.property')
-
-        res = {}
-        res['type'] = line.type
-        res['description'] = line.description
-        res['note'] = line.note
-        if line.type != 'line':
-            if (line.purchase.invoice_method == 'order'
-                    and (all(l.quantity >= 0 for l in line.sale.lines
+        with Transaction().set_user(0, set_context=True):
+            invoice_line = InvoiceLine()
+        invoice_line.type = self.type
+        invoice_line.description = self.description
+        invoice_line.note = self.note
+        if self.type != 'line':
+            if (self.purchase.invoice_method == 'order'
+                    and (all(l.quantity >= 0 for l in self.sale.lines
                             if l.type == 'line')
-                        or all(l.quantity <= 0 for l in line.sale.lines
+                        or all(l.quantity <= 0 for l in self.sale.lines
                             if l.type == 'line'))):
-                return [res]
+                return [invoice_line]
             else:
                 return []
-        if (line.purchase.invoice_method == 'order'
-                or not line.product
-                or line.product.type == 'service'):
-            quantity = line.quantity
+        if (self.purchase.invoice_method == 'order'
+                or not self.product
+                or self.product.type == 'service'):
+            quantity = self.quantity
         else:
             quantity = 0.0
-            for move in line.moves:
+            for move in self.moves:
                 if move.state == 'done':
-                    quantity += uom_obj.compute_qty(move.uom, move.quantity,
-                            line.unit)
+                    quantity += Uom.compute_qty(move.uom, move.quantity,
+                        self.unit)
 
-        skip_ids = set(l.id for i in line.purchase.invoices_recreated
+        skip_ids = set(l.id for i in self.purchase.invoices_recreated
             for l in i.lines)
-        for invoice_line in line.invoice_lines:
+        for invoice_line in self.invoice_lines:
             if invoice_line.type != 'line':
                 continue
             if invoice_line.id not in skip_ids:
-                quantity -= uom_obj.compute_qty(invoice_line.unit,
-                        invoice_line.quantity, line.unit)
-        res['quantity'] = quantity
+                quantity -= Uom.compute_qty(invoice_line.unit,
+                        invoice_line.quantity, self.unit)
+        invoice_line.quantity = quantity
 
-        if res['quantity'] <= 0.0:
+        if invoice_line.quantity <= 0.0:
             return []
-        res['unit'] = line.unit.id
-        res['product'] = line.product.id
-        res['unit_price'] = line.unit_price
-        res['taxes'] = [('set', [x.id for x in line.taxes])]
-        if line.product:
-            res['account'] = line.product.account_expense_used.id
-            if not res['account']:
+        invoice_line.unit = self.unit
+        invoice_line.product = self.product
+        invoice_line.unit_price = self.unit_price
+        invoice_line.taxes = self.taxes
+        if self.product:
+            invoice_line.account = self.product.account_expense_used
+            if not invoice_line.account:
                 self.raise_user_error('missing_account_expense',
-                        error_args=(line.product.rec_name,))
+                        error_args=(self.product.rec_name,))
         else:
             for model in ('product.template', 'product.category'):
-                res['account'] = property_obj.get('account_expense', model)
-                if res['account']:
+                invoice_line.account = Property.get('account_expense', model)
+                if invoice_line.account:
                     break
-            if not res['account']:
+            if not invoice_line.account:
                 self.raise_user_error('missing_account_expense_property')
-        return [res]
+        return [invoice_line]
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, lines, default=None):
         if default is None:
             default = {}
         default = default.copy()
@@ -1319,127 +1127,108 @@
         default['moves_ignored'] = None
         default['moves_recreated'] = None
         default['invoice_lines'] = None
-        return super(PurchaseLine, self).copy(ids, default=default)
+        return super(PurchaseLine, cls).copy(lines, default=default)
 
-    def get_move(self, line):
+    def get_move(self):
         '''
         Return move values for purchase line
         '''
         pool = Pool()
-        uom_obj = pool.get('product.uom')
+        Uom = pool.get('product.uom')
+        Move = pool.get('stock.move')
 
-        vals = {}
-        if line.type != 'line':
+        if self.type != 'line':
             return
-        if not line.product:
+        if not self.product:
             return
-        if line.product.type == 'service':
+        if self.product.type == 'service':
             return
-        skip_ids = set(x.id for x in line.moves_recreated)
-        quantity = line.quantity
-        for move in line.moves:
-            if move.id not in skip_ids:
-                quantity -= uom_obj.compute_qty(move.uom, move.quantity,
-                        line.unit)
+        skip = set(self.moves_recreated)
+        quantity = self.quantity
+        for move in self.moves:
+            if move not in skip:
+                quantity -= Uom.compute_qty(move.uom, move.quantity,
+                    self.unit)
         if quantity <= 0.0:
             return
-        if not line.purchase.party.supplier_location:
+        if not self.purchase.party.supplier_location:
             self.raise_user_error('supplier_location_required')
-        vals['quantity'] = quantity
-        vals['uom'] = line.unit.id
-        vals['product'] = line.product.id
-        vals['from_location'] = line.from_location.id
-        vals['to_location'] = line.to_location.id
-        vals['state'] = 'draft'
-        vals['company'] = line.purchase.company.id
-        vals['unit_price'] = line.unit_price
-        vals['currency'] = line.purchase.currency.id
-        vals['planned_date'] = line.delivery_date
-        return vals
+        with Transaction().set_user(0, set_context=True):
+            move = Move()
+        move.quantity = quantity
+        move.uom = self.unit
+        move.product = self.product
+        move.from_location = self.from_location
+        move.to_location = self.to_location
+        move.state = 'draft'
+        move.company = self.purchase.company
+        move.unit_price = self.unit_price
+        move.currency = self.purchase.currency
+        move.planned_date = self.delivery_date
+        return move
 
-    def create_move(self, line):
+    def create_move(self):
         '''
         Create move line
         '''
-        pool = Pool()
-        move_obj = pool.get('stock.move')
-
-        vals = self.get_move(line)
-        if not vals:
+        move = self.get_move()
+        if not move:
             return
+        move.save()
         with Transaction().set_user(0, set_context=True):
-            move_id = move_obj.create(vals)
-
-            self.write(line.id, {
-                'moves': [('add', move_id)],
-            })
-        return move_id
-
-PurchaseLine()
+            self.write([self], {
+                    'moves': [('add', [move.id])],
+                    })
+        return move
 
 
 class PurchaseLineTax(ModelSQL):
     'Purchase Line - Tax'
-    _name = 'purchase.line-account.tax'
+    __name__ = 'purchase.line-account.tax'
     _table = 'purchase_line_account_tax'
-    _description = __doc__
     line = fields.Many2One('purchase.line', 'Purchase Line',
             ondelete='CASCADE', select=True, required=True,
             domain=[('type', '=', 'line')])
     tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
             select=True, required=True, domain=[('parent', '=', None)])
 
-PurchaseLineTax()
-
 
 class PurchaseLineInvoiceLine(ModelSQL):
     'Purchase Line - Invoice Line'
-    _name = 'purchase.line-account.invoice.line'
+    __name__ = 'purchase.line-account.invoice.line'
     _table = 'purchase_line_invoice_lines_rel'
-    _description = __doc__
     purchase_line = fields.Many2One('purchase.line', 'Purchase Line',
             ondelete='CASCADE', select=True, required=True)
     invoice_line = fields.Many2One('account.invoice.line', 'Invoice Line',
             ondelete='RESTRICT', select=True, required=True)
 
-PurchaseLineInvoiceLine()
-
 
 class PurchaseLineIgnoredMove(ModelSQL):
     'Purchase Line - Ignored Move'
-    _name = 'purchase.line-ignored-stock.move'
+    __name__ = 'purchase.line-ignored-stock.move'
     _table = 'purchase_line_moves_ignored_rel'
-    _description = __doc__
     purchase_line = fields.Many2One('purchase.line', 'Purchase Line',
             ondelete='CASCADE', select=True, required=True)
     move = fields.Many2One('stock.move', 'Move', ondelete='RESTRICT',
             select=True, required=True)
 
-PurchaseLineIgnoredMove()
-
 
 class PurchaseLineRecreatedMove(ModelSQL):
     'Purchase Line - Ignored Move'
-    _name = 'purchase.line-recreated-stock.move'
+    __name__ = 'purchase.line-recreated-stock.move'
     _table = 'purchase_line_moves_recreated_rel'
-    _description = __doc__
     purchase_line = fields.Many2One('purchase.line', 'Purchase Line',
             ondelete='CASCADE', select=True, required=True)
     move = fields.Many2One('stock.move', 'Move', ondelete='RESTRICT',
             select=True, required=True)
 
-PurchaseLineRecreatedMove()
-
 
 class PurchaseReport(CompanyReport):
-    _name = 'purchase.purchase'
+    __name__ = 'purchase.purchase'
 
-PurchaseReport()
 
-
-class Template(ModelSQL, ModelView):
-    _name = "product.template"
-
+class Template:
+    __name__ = "product.template"
     purchasable = fields.Boolean('Purchasable', states={
             'readonly': ~Eval('active', True),
             }, depends=['active'])
@@ -1458,54 +1247,41 @@
         on_change_with=['default_uom', 'purchase_uom', 'purchasable'],
         depends=['active', 'purchasable', 'default_uom_category'])
 
-    def __init__(self):
-        super(Template, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Template, cls).__setup__()
+        cls._error_messages.update({
                 'change_purchase_uom': 'Purchase prices are based ' \
                     'on the purchase uom, are you sure to change it?',
             })
-        self.account_expense = copy.copy(self.account_expense)
-        self.account_expense.states = copy.copy(self.account_expense.states)
         required = ~Eval('account_category') & Eval('purchasable', False)
-        if not self.account_expense.states.get('required'):
-            self.account_expense.states['required'] = required
+        if not cls.account_expense.states.get('required'):
+            cls.account_expense.states['required'] = required
         else:
-            self.account_expense.states['required'] = (
-                self.account_expense.states['required'] | required)
-        if 'account_category' not in self.account_expense.depends:
-            self.account_expense = copy.copy(self.account_expense)
-            self.account_expense.depends = \
-                    copy.copy(self.account_expense.depends)
-            self.account_expense.depends.append('account_category')
-        if 'purchasable' not in self.account_expense.depends:
-            self.account_expense = copy.copy(self.account_expense)
-            self.account_expense.depends = \
-                    copy.copy(self.account_expense.depends)
-            self.account_expense.depends.append('purchasable')
-        self._reset_columns()
+            cls.account_expense.states['required'] = (
+                cls.account_expense.states['required'] | required)
+        if 'account_category' not in cls.account_expense.depends:
+            cls.account_expense.depends.append('account_category')
+        if 'purchasable' not in cls.account_expense.depends:
+            cls.account_expense.depends.append('purchasable')
 
-    def default_purchasable(self):
+    @staticmethod
+    def default_purchasable():
         return Transaction().context.get('purchasable') or False
 
-    def on_change_with_purchase_uom(self, vals):
-        uom_obj = Pool().get('product.uom')
-        res = None
+    def on_change_with_purchase_uom(self):
+        if self.default_uom:
+            if self.purchase_uom:
+                if self.default_uom.category == self.purchase_uom.category:
+                    return self.purchase_uom.id
+                else:
+                    return self.default_uom.id
+            else:
+                return self.default_uom.id
 
-        if vals.get('default_uom'):
-            default_uom = uom_obj.browse(vals['default_uom'])
-            if vals.get('purchase_uom'):
-                purchase_uom = uom_obj.browse(vals['purchase_uom'])
-                if default_uom.category.id == purchase_uom.category.id:
-                    res = purchase_uom.id
-                else:
-                    res = default_uom.id
-            else:
-                res = default_uom.id
-        return res
-
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, templates, vals):
         if vals.get("purchase_uom"):
-            templates = self.browse(ids)
             for template in templates:
                 if not template.purchase_uom:
                     continue
@@ -1514,53 +1290,47 @@
                 for product in template.products:
                     if not product.product_suppliers:
                         continue
-                    self.raise_user_warning(
+                    cls.raise_user_warning(
                             '%s@product_template' % template.id,
                             'change_purchase_uom')
+        super(Template, cls).write(templates, vals)
 
-        return super(Template, self).write(ids, vals)
 
-Template()
+class Product:
+    __name__ = 'product.product'
 
-
-class Product(ModelSQL, ModelView):
-    _name = 'product.product'
-
-    def get_purchase_price(self, ids, quantity=0):
+    @classmethod
+    def get_purchase_price(cls, products, quantity=0):
         '''
         Return purchase price for product ids.
         The context that can have as keys:
             uom: the unit of measure
             supplier: the supplier party id
             currency: the currency id for the returned price
-
-        :param ids: the product ids
-        :param quantity: the quantity of products
-        :return: a dictionary with for each product ids keys the computed price
         '''
         pool = Pool()
-        uom_obj = pool.get('product.uom')
-        user_obj = pool.get('res.user')
-        currency_obj = pool.get('currency.currency')
-        date_obj = pool.get('ir.date')
+        Uom = pool.get('product.uom')
+        User = pool.get('res.user')
+        Currency = pool.get('currency.currency')
+        Date = pool.get('ir.date')
 
-        today = date_obj.today()
+        today = Date.today()
         res = {}
 
         uom = None
         if Transaction().context.get('uom'):
-            uom = uom_obj.browse(Transaction().context['uom'])
+            uom = Uom(Transaction().context['uom'])
 
         currency = None
         if Transaction().context.get('currency'):
-            currency = currency_obj.browse(Transaction().context['currency'])
+            currency = Currency(Transaction().context['currency'])
 
-        user = user_obj.browse(Transaction().user)
+        user = User(Transaction().user)
 
-        for product in self.browse(ids):
+        for product in products:
             res[product.id] = product.cost_price
             default_uom = product.default_uom
-            default_currency = (user.company.currency.id if user.company
+            default_currency = (user.company.currency if user.company
                 else None)
             if not uom:
                 uom = default_uom
@@ -1570,29 +1340,25 @@
                 for product_supplier in product.product_suppliers:
                     if product_supplier.party.id == supplier_id:
                         for price in product_supplier.prices:
-                            if uom_obj.compute_qty(product.purchase_uom,
+                            if Uom.compute_qty(product.purchase_uom,
                                     price.quantity, uom) <= quantity:
                                 res[product.id] = price.unit_price
                                 default_uom = product.purchase_uom
-                                default_currency = product_supplier.currency.id
+                                default_currency = product_supplier.currency
                         break
-            res[product.id] = uom_obj.compute_price(default_uom,
-                    res[product.id], uom)
+            res[product.id] = Uom.compute_price(default_uom, res[product.id],
+                uom)
             if currency and default_currency:
                 date = Transaction().context.get('purchase_date') or today
                 with Transaction().set_context(date=date):
-                    res[product.id] = currency_obj.compute(default_currency,
-                        res[product.id], currency.id, round=False)
+                    res[product.id] = Currency.compute(default_currency,
+                        res[product.id], currency, round=False)
         return res
 
-Product()
-
 
 class ProductSupplier(ModelSQL, ModelView):
     'Product Supplier'
-    _name = 'purchase.product_supplier'
-    _description = __doc__
-
+    __name__ = 'purchase.product_supplier'
     product = fields.Many2One('product.template', 'Product', required=True,
             ondelete='CASCADE', select=True)
     party = fields.Many2One('party.party', 'Supplier', required=True,
@@ -1615,188 +1381,173 @@
     currency = fields.Many2One('currency.currency', 'Currency', required=True,
         ondelete='RESTRICT')
 
-    def __init__(self):
-        super(ProductSupplier, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ProductSupplier, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 2.2 new field currency
         created_currency = table.column_exist('currency')
 
-        super(ProductSupplier, self).init(module_name)
+        super(ProductSupplier, cls).__register__(module_name)
 
         # Migration from 2.2 fill currency
         if not created_currency:
-            company_obj = Pool().get('company.company')
+            Company = Pool().get('company.company')
             limit = cursor.IN_MAX
-            cursor.execute('SELECT count(id) FROM "' + self._table + '"')
+            cursor.execute('SELECT count(id) FROM "' + cls._table + '"')
             product_supplier_count, = cursor.fetchone()
             for offset in range(0, product_supplier_count, limit):
                 cursor.execute(cursor.limit_clause(
                         'SELECT p.id, c.currency '
-                        'FROM "' + self._table + '" AS p '
-                        'INNER JOIN "' + company_obj._table + '" AS c '
+                        'FROM "' + cls._table + '" AS p '
+                        'INNER JOIN "' + Company._table + '" AS c '
                             'ON p.company = c.id '
                         'ORDER BY p.id',
                         limit, offset))
                 for product_supplier_id, currency_id in cursor.fetchall():
-                    cursor.execute('UPDATE "' + self._table + '" '
+                    cursor.execute('UPDATE "' + cls._table + '" '
                         'SET currency = %s '
                         'WHERE id = %s', (currency_id, product_supplier_id))
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_currency(self):
-        company_obj = Pool().get('company.company')
-        company = None
+    @staticmethod
+    def default_currency():
+        Company = Pool().get('company.company')
         if Transaction().context.get('company'):
-            company = company_obj.browse(Transaction().context['company'])
+            company = Company(Transaction().context['company'])
             return company.currency.id
 
-    def on_change_party(self, values):
+    def on_change_party(self):
         cursor = Transaction().cursor
         changes = {
             'currency': self.default_currency(),
             }
-        if values.get('party'):
+        if self.party:
             cursor.execute('SELECT currency FROM "' + self._table + '" '
                 'WHERE party = %s '
                 'GROUP BY currency '
-                'ORDER BY COUNT(1) DESC', (values['party'],))
+                'ORDER BY COUNT(1) DESC', (self.party.id,))
             row = cursor.fetchone()
             if row:
                 changes['currency'], = row
         return changes
 
-    def compute_supply_date(self, product_supplier, date=None):
+    def compute_supply_date(self, date=None):
         '''
         Compute the supply date for the Product Supplier at the given date
             and the next supply date
-
-        :param product_supplier: a BrowseRecord of the Product Supplier
-        :param date: the date of the purchase if None the current date
-        :return: the supply date
         '''
-        date_obj = Pool().get('ir.date')
+        Date = Pool().get('ir.date')
 
         if not date:
-            date = date_obj.today()
-        if not product_supplier.delivery_time:
+            date = Date.today()
+        if not self.delivery_time:
             return datetime.date.max
-        return date + datetime.timedelta(product_supplier.delivery_time)
+        return date + datetime.timedelta(self.delivery_time)
 
-    def compute_purchase_date(self, product_supplier, date):
+    def compute_purchase_date(self, date):
         '''
         Compute the purchase date for the Product Supplier at the given date
+        '''
+        Date = Pool().get('ir.date')
 
-        :param product_supplier: a BrowseRecord of the Product Supplier
-        :param date: the date of the supply
-        :return: the purchase date
-        '''
-        date_obj = Pool().get('ir.date')
-
-        if not product_supplier.delivery_time:
-            return date_obj.today()
-        return date - datetime.timedelta(product_supplier.delivery_time)
-
-ProductSupplier()
+        if not self.delivery_time:
+            return Date.today()
+        return date - datetime.timedelta(self.delivery_time)
 
 
 class ProductSupplierPrice(ModelSQL, ModelView):
     'Product Supplier Price'
-    _name = 'purchase.product_supplier.price'
-    _description = __doc__
-
+    __name__ = 'purchase.product_supplier.price'
     product_supplier = fields.Many2One('purchase.product_supplier',
             'Supplier', required=True, ondelete='CASCADE')
     quantity = fields.Float('Quantity', required=True, help='Minimal quantity')
     unit_price = fields.Numeric('Unit Price', required=True, digits=(16, 4))
 
-    def __init__(self):
-        super(ProductSupplierPrice, self).__init__()
-        self._order.insert(0, ('quantity', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ProductSupplierPrice, cls).__setup__()
+        cls._order.insert(0, ('quantity', 'ASC'))
 
-    def default_quantity(self):
+    @staticmethod
+    def default_quantity():
         return 0.0
 
-ProductSupplierPrice()
 
+class ShipmentIn:
+    __name__ = 'stock.shipment.in'
 
-class ShipmentIn(ModelSQL, ModelView):
-    _name = 'stock.shipment.in'
-
-    def __init__(self):
-        super(ShipmentIn, self).__init__()
-        self.incoming_moves = copy.copy(self.incoming_moves)
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentIn, cls).__setup__()
         add_remove = [
             ('supplier', '=', Eval('supplier')),
-        ]
-        if not self.incoming_moves.add_remove:
-            self.incoming_moves.add_remove = add_remove
+            ]
+        if not cls.incoming_moves.add_remove:
+            cls.incoming_moves.add_remove = add_remove
         else:
-            self.incoming_moves.add_remove = \
-                    copy.copy(self.incoming_moves.add_remove)
-            self.incoming_moves.add_remove = [
+            cls.incoming_moves.add_remove = [
                 add_remove,
-                self.incoming_moves.add_remove,
-            ]
-        self._reset_columns()
+                cls.incoming_moves.add_remove,
+                ]
 
-        self._error_messages.update({
+        cls._error_messages.update({
                 'reset_move': 'You cannot reset to draft a move generated '\
                     'by a purchase.',
             })
 
-    def write(self, ids, vals):
-        purchase_obj = Pool().get('purchase.purchase')
-        purchase_line_obj = Pool().get('purchase.line')
+    @classmethod
+    def write(cls, shipments, vals):
+        pool = Pool()
+        Purchase = pool.get('purchase.purchase')
+        PurchaseLine = pool.get('purchase.line')
 
-        res = super(ShipmentIn, self).write(ids, vals)
+        super(ShipmentIn, cls).write(shipments, vals)
 
         if 'state' in vals and vals['state'] in ('received', 'cancel'):
-            purchase_ids = []
+            purchases = []
             move_ids = []
-            if isinstance(ids, (int, long)):
-                ids = [ids]
-            for shipment in self.browse(ids):
+            for shipment in shipments:
                 move_ids.extend([x.id for x in shipment.incoming_moves])
 
-            purchase_line_ids = purchase_line_obj.search([
-                ('moves', 'in', move_ids),
-                ])
-            if purchase_line_ids:
-                for purchase_line in purchase_line_obj.browse(
-                        purchase_line_ids):
-                    if purchase_line.purchase.id not in purchase_ids:
-                        purchase_ids.append(purchase_line.purchase.id)
+            purchase_lines = PurchaseLine.search([
+                    ('moves', 'in', move_ids),
+                    ])
+            if purchase_lines:
+                for purchase_line in purchase_lines:
+                    if purchase_line.purchase not in purchases:
+                        purchases.append(purchase_line.purchase)
 
             with Transaction().set_user(0, set_context=True):
-                purchase_obj.process(purchase_ids)
-        return res
+                purchases = Purchase.browse([p.id for p in purchases])
+            Purchase.process(purchases)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
-        for shipment in self.browse(ids):
+    def draft(cls, shipments):
+        for shipment in shipments:
             for move in shipment.incoming_moves:
                 if move.state == 'cancel' and move.purchase_line:
-                    self.raise_user_error('reset_move')
+                    cls.raise_user_error('reset_move')
 
-        return super(ShipmentIn, self).draft(ids)
-
-ShipmentIn()
+        return super(ShipmentIn, cls).draft(shipments)
 
 
 class Move(ModelSQL, ModelView):
-    _name = 'stock.move'
-
+    __name__ = 'stock.move'
     purchase_line = fields.Many2One('purchase.line', 'Purchase Line',
         select=True, states={
             'readonly': Eval('state') != 'draft',
@@ -1829,7 +1580,7 @@
                 'invisible': ~Eval('purchase_visible', False),
                 }, depends=['purchase_visible']), 'get_purchase_fields')
     purchase_visible = fields.Function(fields.Boolean('Purchase Visible',
-        on_change_with=['from_location']), 'get_purchase_visible')
+        on_change_with=['from_location']), 'on_change_with_purchase_visible')
     supplier = fields.Function(fields.Many2One('party.party', 'Supplier',
         select=True), 'get_supplier', searcher='search_supplier')
     purchase_exception_state = fields.Function(fields.Selection([
@@ -1838,203 +1589,102 @@
         ('recreated', 'Recreated'),
         ], 'Exception State'), 'get_purchase_exception_state')
 
-    def get_purchase(self, ids, name):
-        res = {}
-        for move in self.browse(ids):
-            res[move.id] = None
-            if move.purchase_line:
-                res[move.id] = move.purchase_line.purchase.id
-        return res
+    def get_purchase(self, name):
+        if self.purchase_line:
+            return self.purchase_line.purchase.id
 
-    def get_purchase_exception_state(self, ids, name):
-        res = {}.fromkeys(ids, '')
-        for move in self.browse(ids):
-            if not move.purchase_line:
-                continue
-            if move.id in (x.id for x in move.purchase_line.moves_recreated):
-                res[move.id] = 'recreated'
-            if move.id in (x.id for x in move.purchase_line.moves_ignored):
-                res[move.id] = 'ignored'
-        return res
+    def get_purchase_exception_state(self, name):
+        if not self.purchase_line:
+            return ''
+        if self in self.purchase_line.moves_recreated:
+            return 'recreated'
+        if self in self.purchase_line.moves_ignored:
+            return 'ignored'
 
-    def search_purchase(self, name, clause):
+    @classmethod
+    def search_purchase(cls, name, clause):
         return [('purchase_line.' + name,) + tuple(clause[1:])]
 
-    def get_purchase_fields(self, ids, names):
-        res = {}
-        for name in names:
-            res[name] = {}
+    def get_purchase_fields(self, name):
+        if self.purchase_line:
+            if name[9:] == 'currency':
+                return self.purchase_line.purchase.currency.id
+            elif name[9:] in ('quantity', 'unit_digits', 'unit_price'):
+                return getattr(self.purchase_line, name[9:])
+            else:
+                return getattr(self.purchase_line, name[9:]).id
+        else:
+            if name[9:] == 'quantity':
+                return 0.0
+            elif name[9:] == 'unit_digits':
+                return 2
 
-        for move in self.browse(ids):
-            for name in res.keys():
-                if name[9:] == 'quantity':
-                    res[name][move.id] = 0.0
-                elif name[9:] == 'unit_digits':
-                    res[name][move.id] = 2
-                else:
-                    res[name][move.id] = None
-            if move.purchase_line:
-                for name in res.keys():
-                    if name[9:] == 'currency':
-                        res[name][move.id] = move.purchase_line.\
-                                purchase.currency.id
-                    elif name[9:] in ('quantity', 'unit_digits', 'unit_price'):
-                        res[name][move.id] = move.purchase_line[name[9:]]
-                    else:
-                        res[name][move.id] = move.purchase_line[name[9:]].id
-        return res
-
-    def on_change_with_purchase_visible(self, vals):
-        location_obj = Pool().get('stock.location')
-        if vals.get('from_location'):
-            from_location = location_obj.browse(vals['from_location'])
-            if from_location.type == 'supplier':
+    def on_change_with_purchase_visible(self, name=None):
+        if self.from_location:
+            if self.from_location.type == 'supplier':
                 return True
         return False
 
-    def get_purchase_visible(self, ids, name):
-        res = {}
-        for move in self.browse(ids):
-            res[move.id] = False
-            if move.from_location.type == 'supplier':
-                res[move.id] = True
-        return res
+    def get_supplier(self, name):
+        if self.purchase_line:
+            return self.purchase_line.purchase.party.id
 
-    def get_supplier(self, ids, name):
-        res = {}
-        for move in self.browse(ids):
-            res[move.id] = None
-            if move.purchase_line:
-                res[move.id] = move.purchase_line.purchase.party.id
-        return res
-
-    def search_supplier(self, name, clause):
+    @classmethod
+    def search_supplier(cls, name, clause):
         return [('purchase_line.purchase.party',) + tuple(clause[1:])]
 
-    def write(self, ids, vals):
-        purchase_obj = Pool().get('purchase.purchase')
-        purchase_line_obj = Pool().get('purchase.line')
+    @classmethod
+    def write(cls, moves, vals):
+        pool = Pool()
+        Purchase = pool.get('purchase.purchase')
+        PurchaseLine = pool.get('purchase.line')
 
-        res = super(Move, self).write(ids, vals)
+        super(Move, cls).write(moves, vals)
         if 'state' in vals and vals['state'] in ('cancel',):
-            if isinstance(ids, (int, long)):
-                ids = [ids]
-            purchase_ids = set()
-            purchase_line_ids = purchase_line_obj.search([
-                ('moves', 'in', ids),
+            purchases = set()
+            purchase_lines = PurchaseLine.search([
+                    ('moves', 'in', [m.id for m in moves]),
+                    ])
+            if purchase_lines:
+                for purchase_line in purchase_lines:
+                    purchases.add(purchase_line.purchase)
+            if purchases:
+                with Transaction().set_user(0, set_context=True):
+                    purchases = Purchase.browse([p.id for p in purchases])
+                Purchase.process(purchases)
+
+    @classmethod
+    def delete(cls, moves):
+        pool = Pool()
+        Purchase = pool.get('purchase.purchase')
+        PurchaseLine = pool.get('purchase.line')
+
+        purchases = set()
+        purchase_lines = PurchaseLine.search([
+                ('moves', 'in', [m.id for m in moves]),
                 ])
-            if purchase_line_ids:
-                for purchase_line in purchase_line_obj.browse(
-                        purchase_line_ids):
-                    purchase_ids.add(purchase_line.purchase.id)
-            if purchase_ids:
+
+        super(Move, cls).delete(moves)
+
+        if purchase_lines:
+            for purchase_line in purchase_lines:
+                purchases.add(purchase_line.purchase)
+            if purchases:
                 with Transaction().set_user(0, set_context=True):
-                    purchase_obj.process(list(purchase_ids))
-        return res
-
-    def delete(self, ids):
-        purchase_obj = Pool().get('purchase.purchase')
-        purchase_line_obj = Pool().get('purchase.line')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        purchase_ids = set()
-        purchase_line_ids = purchase_line_obj.search([
-            ('moves', 'in', ids),
-            ])
-
-        res = super(Move, self).delete(ids)
-
-        if purchase_line_ids:
-            for purchase_line in purchase_line_obj.browse(purchase_line_ids):
-                purchase_ids.add(purchase_line.purchase.id)
-            if purchase_ids:
-                with Transaction().set_user(0, set_context=True):
-                    purchase_obj.process(list(purchase_ids))
-        return res
-Move()
-
-
-class Invoice(ModelSQL, ModelView):
-    _name = 'account.invoice'
-
-    purchase_exception_state = fields.Function(fields.Selection([
-        ('', ''),
-        ('ignored', 'Ignored'),
-        ('recreated', 'Recreated'),
-        ], 'Exception State'), 'get_purchase_exception_state')
-
-    def __init__(self):
-        super(Invoice, self).__init__()
-        self._error_messages.update({
-                'delete_purchase_invoice': 'You can not delete invoices ' \
-                    'that come from a purchase!',
-                'reset_invoice_purchase': 'You cannot reset to draft ' \
-                        'an invoice generated by a purchase.',
-            })
-
-    @ModelView.button
-    @Workflow.transition('draft')
-    def draft(self, ids):
-        purchase_obj = Pool().get('purchase.purchase')
-        purchase_ids = purchase_obj.search([
-            ('invoices', 'in', ids),
-            ])
-
-        if purchase_ids:
-            self.raise_user_error('reset_invoice_purchase')
-
-        return super(Invoice, self).draft(ids)
-
-    def get_purchase_exception_state(self, ids, name):
-        purchase_obj = Pool().get('purchase.purchase')
-        purchase_ids = purchase_obj.search([
-            ('invoices', 'in', ids),
-            ])
-
-        purchases = purchase_obj.browse(purchase_ids)
-
-        recreated_ids = tuple(i.id for p in purchases
-            for i in p.invoices_recreated)
-        ignored_ids = tuple(i.id for p in purchases
-            for i in p.invoices_ignored)
-
-        res = {}.fromkeys(ids, '')
-        for invoice in self.browse(ids):
-            if invoice.id in recreated_ids:
-                res[invoice.id] = 'recreated'
-            elif invoice.id in ignored_ids:
-                res[invoice.id] = 'ignored'
-
-        return res
-
-    def delete(self, ids):
-        cursor = Transaction().cursor
-        if not ids:
-            return True
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        cursor.execute('SELECT id FROM purchase_invoices_rel ' \
-                'WHERE invoice IN (' + ','.join(('%s',) * len(ids)) + ')',
-                ids)
-        if cursor.fetchone():
-            self.raise_user_error('delete_purchase_invoice')
-        return super(Invoice, self).delete(ids)
-
-Invoice()
+                    purchases = Purchase.browse([p.id for p in purchases])
+                Purchase.process(purchases)
 
 
 class OpenSupplier(Wizard):
     'Open Suppliers'
-    _name = 'purchase.open_supplier'
+    __name__ = 'purchase.open_supplier'
     start_state = 'open_'
     open_ = StateAction('party.act_party_form')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        wizard_obj = pool.get('ir.action.wizard')
+        ModelData = pool.get('ir.model.data')
+        Wizard = pool.get('ir.action.wizard')
         cursor = Transaction().cursor
 
         cursor.execute("SELECT DISTINCT(party) FROM purchase_purchase")
@@ -2042,25 +1692,20 @@
         action['pyson_domain'] = PYSONEncoder().encode(
             [('id', 'in', supplier_ids)])
 
-        model_data_ids = model_data_obj.search([
-            ('fs_id', '=', 'act_open_supplier'),
-            ('module', '=', 'purchase'),
-            ('inherit', '=', None),
-            ], limit=1)
-        model_data = model_data_obj.browse(model_data_ids[0])
-        wizard = wizard_obj.browse(model_data.db_id)
+        model_data, = ModelData.search([
+                ('fs_id', '=', 'act_open_supplier'),
+                ('module', '=', 'purchase'),
+                ('inherit', '=', None),
+                ], limit=1)
+        wizard = Wizard.browse(model_data.db_id)
 
         action['name'] = wizard.name
         return action, {}
 
-OpenSupplier()
-
 
 class HandleShipmentExceptionAsk(ModelView):
     'Handle Shipment Exception'
-    _name = 'purchase.handle.shipment.exception.ask'
-    _description = __doc__
-
+    __name__ = 'purchase.handle.shipment.exception.ask'
     recreate_moves = fields.Many2Many(
         'stock.move', None, None, 'Recreate Moves',
         domain=[('id', 'in', Eval('domain_moves'))], depends=['domain_moves'],
@@ -2069,21 +1714,20 @@
     domain_moves = fields.Many2Many(
         'stock.move', None, None, 'Domain Moves')
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         cursor.execute("UPDATE ir_model "\
                 "SET model = REPLACE(model, 'packing', 'shipment') "\
                 "WHERE model like '%%packing%%' AND module = %s",
                 (module_name,))
-        super(HandleShipmentExceptionAsk, self).init(module_name)
-
-HandleShipmentExceptionAsk()
+        super(HandleShipmentExceptionAsk, cls).__register__(module_name)
 
 
 class HandleShipmentException(Wizard):
     'Handle Shipment Exception'
-    _name = 'purchase.handle.shipment.exception'
+    __name__ = 'purchase.handle.shipment.exception'
     start_state = 'ask'
     ask = StateView('purchase.handle.shipment.exception.ask',
         'purchase.handle_shipment_exception_ask_view_form', [
@@ -2092,61 +1736,56 @@
             ])
     handle = StateTransition()
 
-    def default_ask(self, session, fields):
-        purchase_obj = Pool().get('purchase.purchase')
+    def default_ask(self, fields):
+        Purchase = Pool().get('purchase.purchase')
 
-        purchase = purchase_obj.browse(Transaction().context['active_id'])
+        purchase = Purchase(Transaction().context['active_id'])
 
         moves = []
         for line in purchase.lines:
-            skip_ids = set(x.id for x in line.moves_ignored + \
-                               line.moves_recreated)
+            skip = set(line.moves_ignored + line.moves_recreated)
             for move in line.moves:
-                if move.state == 'cancel' and move.id not in skip_ids:
+                if move.state == 'cancel' and move not in skip:
                     moves.append(move.id)
         return {
             'to_recreate': moves,
             'domain_moves': moves,
             }
 
-    def transition_handle(self, session):
+    def transition_handle(self):
         pool = Pool()
-        purchase_obj = pool.get('purchase.purchase')
-        purchase_line_obj = pool.get('purchase.line')
-        to_recreate = [x.id for x in session.ask.recreate_moves]
-        domain_moves = [x.id for x in session.ask.domain_moves]
+        Purchase = pool.get('purchase.purchase')
+        PurchaseLine = pool.get('purchase.line')
+        to_recreate = self.ask.recreate_moves
+        domain_moves = self.ask.domain_moves
 
-        purchase = purchase_obj.browse(Transaction().context['active_id'])
+        purchase = Purchase(Transaction().context['active_id'])
 
         for line in purchase.lines:
             moves_ignored = []
             moves_recreated = []
-            skip_ids = set(x.id for x in line.moves_ignored)
-            skip_ids.update(x.id for x in line.moves_recreated)
+            skip = set(line.moves_ignored)
+            skip.update(line.moves_recreated)
             for move in line.moves:
-                if move.id not in domain_moves or move.id in skip_ids:
+                if move not in domain_moves or move in skip:
                     continue
-                if move.id in to_recreate:
+                if move in to_recreate:
                     moves_recreated.append(move.id)
                 else:
                     moves_ignored.append(move.id)
 
-            purchase_line_obj.write(line.id, {
+            PurchaseLine.write([line], {
                 'moves_ignored': [('add', moves_ignored)],
                 'moves_recreated': [('add', moves_recreated)],
                 })
 
-        purchase_obj.process([purchase.id])
+        Purchase.process([purchase])
         return 'end'
 
-HandleShipmentException()
-
 
 class HandleInvoiceExceptionAsk(ModelView):
     'Handle Invoice Exception'
-    _name = 'purchase.handle.invoice.exception.ask'
-    _description = __doc__
-
+    __name__ = 'purchase.handle.invoice.exception.ask'
     recreate_invoices = fields.Many2Many(
         'account.invoice', None, None, 'Recreate Invoices',
         domain=[('id', 'in', Eval('domain_invoices'))],
@@ -2156,12 +1795,10 @@
     domain_invoices = fields.Many2Many(
         'account.invoice', None, None, 'Domain Invoices')
 
-HandleInvoiceExceptionAsk()
-
 
 class HandleInvoiceException(Wizard):
     'Handle Invoice Exception'
-    _name = 'purchase.handle.invoice.exception'
+    __name__ = 'purchase.handle.invoice.exception'
     start_state = 'ask'
     ask = StateView('purchase.handle.invoice.exception.ask',
         'purchase.handle_invoice_exception_ask_view_form', [
@@ -2170,46 +1807,44 @@
             ])
     handle = StateTransition()
 
-    def default_ask(self, session, fields):
-        purchase_obj = Pool().get('purchase.purchase')
+    def default_ask(self, fields):
+        Purchase = Pool().get('purchase.purchase')
 
-        purchase = purchase_obj.browse(Transaction().context['active_id'])
-        skip_ids = set(x.id for x in purchase.invoices_ignored)
-        skip_ids.update(x.id for x in purchase.invoices_recreated)
+        purchase = Purchase(Transaction().context['active_id'])
+        skip = set(purchase.invoices_ignored)
+        skip.update(purchase.invoices_recreated)
         invoices = []
         for invoice in purchase.invoices:
-            if invoice.state == 'cancel' and invoice.id not in skip_ids:
+            if invoice.state == 'cancel' and invoice not in skip:
                 invoices.append(invoice.id)
         return {
             'to_recreate': invoices,
             'domain_invoices': invoices,
             }
 
-    def transition_handle(self, session):
-        purchase_obj = Pool().get('purchase.purchase')
-        to_recreate = [x.id for x in session.ask.recreate_invoices]
-        domain_invoices = [x.id for x in session.ask.domain_invoices]
+    def transition_handle(self):
+        Purchase = Pool().get('purchase.purchase')
+        to_recreate = self.ask.recreate_invoices
+        domain_invoices = self.ask.domain_invoices
 
-        purchase = purchase_obj.browse(Transaction().context['active_id'])
+        purchase = Purchase(Transaction().context['active_id'])
 
-        skip_ids = set(x.id for x in purchase.invoices_ignored)
-        skip_ids.update(x.id for x in purchase.invoices_recreated)
+        skip = set(purchase.invoices_ignored)
+        skip.update(purchase.invoices_recreated)
         invoices_ignored = []
         invoices_recreated = []
         for invoice in purchase.invoices:
-            if invoice.id not in domain_invoices or invoice.id in skip_ids:
+            if invoice not in domain_invoices or invoice in skip:
                 continue
             if invoice.id in to_recreate:
                 invoices_recreated.append(invoice.id)
             else:
                 invoices_ignored.append(invoice.id)
 
-        purchase_obj.write(purchase.id, {
+        Purchase.write([purchase], {
             'invoices_ignored': [('add', invoices_ignored)],
             'invoices_recreated': [('add', invoices_recreated)],
             })
 
-        purchase_obj.process([purchase.id])
+        Purchase.process([purchase])
         return 'end'
-
-HandleInvoiceException()
diff -r 5e0d53cac617 trytond/modules/purchase/stock.py
--- a/trytond/modules/purchase/stock.py	Mon Sep 10 15:28:28 2012 +0200
+++ b/trytond/modules/purchase/stock.py	Mon Sep 24 20:36:42 2012 +0200
@@ -7,23 +7,22 @@
 from trytond.transaction import Transaction
 from trytond.pyson import PYSONDecoder, PYSONEncoder
 
+__all__ = ['OpenProductQuantitiesByWarehouse']
+
 
 class OpenProductQuantitiesByWarehouse(Wizard):
-    _name = 'stock.product_quantities_warehouse'
+    __name__ = 'stock.product_quantities_warehouse'
 
-    def do_open_(self, session, action):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        product_supplier_obj = pool.get('purchase.product_supplier')
+    def do_open_(self, action):
+        Product = Pool().get('product.product')
 
         action, data = super(OpenProductQuantitiesByWarehouse,
-            self).do_open_(session, action)
+            self).do_open_(action)
 
-        product = product_obj.browse(Transaction().context['active_id'])
+        product = Product(Transaction().context['active_id'])
         if product.product_suppliers:
             product_supplier = product.product_suppliers[0]
-            supply_date = \
-                product_supplier_obj.compute_supply_date(product_supplier)
+            supply_date = product_supplier.compute_supply_date()
             if supply_date != datetime.date.max:
                 search_value = \
                     PYSONDecoder().decode(action['pyson_search_value'])
@@ -35,5 +34,3 @@
                 action['pyson_search_value'] = PYSONEncoder().encode(
                     search_value)
         return action, data
-
-OpenProductQuantitiesByWarehouse()
diff -r d165adfd31c2 trytond/modules/purchase_invoice_line_standalone/__init__.py
--- a/trytond/modules/purchase_invoice_line_standalone/__init__.py	Thu Jul 26 00:07:52 2012 +0200
+++ b/trytond/modules/purchase_invoice_line_standalone/__init__.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,5 +1,15 @@
 #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.pool import Pool
 from .purchase import *
 from .invoice import *
+
+
+def register():
+    Pool.register(
+        Purchase,
+        PurchaseInvoiceLine,
+        PurchaseIgnoredInvoiceLine,
+        InvoiceLine,
+        module='purchase_invoice_line_standalone', type_='model')
diff -r d165adfd31c2 trytond/modules/purchase_invoice_line_standalone/invoice.py
--- a/trytond/modules/purchase_invoice_line_standalone/invoice.py	Thu Jul 26 00:07:52 2012 +0200
+++ b/trytond/modules/purchase_invoice_line_standalone/invoice.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,60 +1,56 @@
 #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 ModelView, ModelSQL
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['InvoiceLine']
+__metaclass__ = PoolMeta
 
-class InvoiceLine(ModelSQL, ModelView):
-    _name = 'account.invoice.line'
 
-    def __init__(self):
-        super(InvoiceLine, self).__init__()
-        self._error_messages.update({
+class InvoiceLine:
+    __name__ = 'account.invoice.line'
+
+    @classmethod
+    def __setup__(cls):
+        super(InvoiceLine, cls).__setup__()
+        cls._error_messages.update({
             'delete_purchase_invoice_line': 'You can not delete ' \
                     'invoice lines that comes from a purchase!',
             })
 
-    def write(self, ids, vals):
-        purchase_obj = Pool().get('purchase.purchase')
+    @classmethod
+    def write(cls, lines, vals):
+        Purchase = Pool().get('purchase.purchase')
 
         if 'invoice' in vals:
-            if isinstance(ids, (int, long)):
-                ids = [ids]
-
-            purchase_ids = purchase_obj.search([
-                ('invoice_lines', 'in', ids),
-                ])
+            purchases = Purchase.search([
+                    ('invoice_lines', 'in', [l.id for l in lines]),
+                    ])
             if vals['invoice']:
-                purchase_obj.write(purchase_ids, {
+                Purchase.write(purchases, {
                     'invoices': [('add', vals['invoice'])],
                     })
             else:
-                purchases = purchase_obj.browse(purchase_ids)
                 for purchase in purchases:
-                    invoice_ids = list(set([x.invoice.id for x \
-                            in purchase.invoice_lines \
-                            if x.invoice and x.id in ids]) - \
-                            set([x.invoice.id for x \
-                            in purchase.invoice_lines \
-                            if x.invoice and x.id not in ids]))
-                    purchase_obj.write(purchase.id, {
+                    invoice_ids = list(set([x.invoice.id for x
+                                in purchase.invoice_lines
+                                if x.invoice and x.id in lines])
+                        - set([x.invoice.id for x
+                                in purchase.invoice_lines
+                                if x.invoice and x.id not in lines]))
+                    Purchase.write([purchase], {
                         'invoices': [('unlink', invoice_ids)],
                         })
 
-        return super(InvoiceLine, self).write(ids, vals)
+        return super(InvoiceLine, cls).write(lines, vals)
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, lines):
         cursor = Transaction().cursor
-        if not ids:
-            return True
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        cursor.execute('SELECT id FROM purchase_invoice_line_rel ' \
-                'WHERE line IN (' + ','.join(['%s' for x in ids]) + ')',
-                ids)
-        if cursor.rowcount:
-            self.raise_user_error('delete_purchase_invoice_line')
-        return super(InvoiceLine, self).delete(ids)
-
-InvoiceLine()
+        if lines:
+            cursor.execute('SELECT id FROM purchase_invoice_line_rel ' \
+                    'WHERE line IN (' + ','.join(['%s' for x in lines]) + ')',
+                    [l.id for l in lines])
+            if cursor.rowcount:
+                cls.raise_user_error('delete_purchase_invoice_line')
+        super(InvoiceLine, cls).delete(lines)
diff -r d165adfd31c2 trytond/modules/purchase_invoice_line_standalone/purchase.py
--- a/trytond/modules/purchase_invoice_line_standalone/purchase.py	Thu Jul 26 00:07:52 2012 +0200
+++ b/trytond/modules/purchase_invoice_line_standalone/purchase.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,20 +1,23 @@
 #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 ModelView, ModelSQL, fields
+from trytond.model import ModelSQL, fields
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Purchase', 'PurchaseInvoiceLine', 'PurchaseIgnoredInvoiceLine']
+__metaclass__ = PoolMeta
 
-class Purchase(ModelSQL, ModelView):
-    _name = 'purchase.purchase'
 
+class Purchase:
+    __name__ = 'purchase.purchase'
     invoice_lines = fields.Many2Many('purchase.purchase-account.invoice.line',
             'purchase', 'line', 'Invoice Lines', readonly=True)
     invoice_lines_ignored = fields.Many2Many(
             'purchase.purchase-ignored-account.invoice.line',
             'purchase', 'invoice', 'Invoice Lines Ignored', readonly=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         cursor.execute("UPDATE ir_model_data "\
@@ -22,40 +25,39 @@
                 "WHERE fs_id like '%%packing%%' "\
                     "AND module = %s", (module_name,))
 
-        super(Purchase, self).init(module_name)
+        super(Purchase, cls).__register__(module_name)
 
-    def create_invoice(self, purchase):
-        invoice_obj = Pool().get('account.invoice')
-        invoice_line_obj = Pool().get('account.invoice.line')
+    def create_invoice(self):
+        pool = Pool()
+        Invoice = pool.get('account.invoice')
+        InvoiceLine = pool.get('account.invoice.line')
 
-        invoice_id = super(Purchase, self).create_invoice(purchase)
+        invoice = super(Purchase, self).create_invoice()
 
-        if invoice_id:
-            invoice = invoice_obj.browse(invoice_id)
-            line_ids = [x.id for x in invoice.lines]
+        if invoice:
+            invoice_line_ids = [l.id for l in invoice.lines]
             with Transaction().set_user(0, set_context=True):
-                invoice_line_obj.write(line_ids, {
+                InvoiceLine.write(invoice.lines, {
                     'invoice': None,
                     'invoice_type': invoice.type,
                     'party': invoice.party,
                     'currency': invoice.currency.id,
                     'company': invoice.company.id,
                     })
-            self.write(purchase.id, {
-                'invoices': [('unlink', invoice_id)],
-                'invoice_lines': [('add', line_ids)],
+            self.write([self], {
+                'invoices': [('unlink', [invoice.id])],
+                'invoice_lines': [('add', invoice_line_ids)],
                 })
             with Transaction().set_user(0, set_context=True):
-                invoice_obj.cancel([invoice_id])
-                invoice_obj.delete(invoice_id)
+                Invoice.cancel([invoice])
+                Invoice.delete([invoice])
             return None
-        return invoice_id
+        return invoice
 
-    def get_invoice_state(self, purchase):
-        state = super(Purchase, self).get_invoice_state(purchase)
-        skip_ids = set(x.id for x in purchase.invoice_lines_ignored)
-        invoice_lines = [l for l in purchase.invoice_lines
-            if l.id not in skip_ids]
+    def get_invoice_state(self):
+        state = super(Purchase, self).get_invoice_state()
+        skips = set(x.id for x in self.invoice_lines_ignored)
+        invoice_lines = [l for l in self.invoice_lines if l not in skips]
         if invoice_lines:
             if any(l.invoice and l.invoice.state == 'cancel'
                     for l in invoice_lines):
@@ -66,52 +68,44 @@
                 return 'paid'
         return state
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, purchases, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default['invoice_lines'] = None
         default['invoice_lines_ignored'] = None
-        return super(Purchase, self).copy(ids, default=default)
+        return super(Purchase, cls).copy(purchases, default=default)
 
-    def ignore_invoice_exception(self, purchase_id):
-        super(Purchase, self).ignore_invoice_exception(purchase_id)
-        purchase = self.browse(purchase_id)
-        invoice_line_ids = []
-        for invoice_line in purchase.invoice_lines:
+    def ignore_invoice_exception(self):
+        super(Purchase, self).ignore_invoice_exception()
+        invoice_lines = []
+        for invoice_line in self.invoice_lines:
             if invoice_line.invoice \
                     and invoice_line.invoice.state == 'cancel':
-                invoice_line_ids.append(invoice_line.id)
-        if invoice_line_ids:
-            self.write(purchase_id, {
+                invoice_lines.append(invoice_line)
+        if invoice_lines:
+            self.write([self], {
                     'invoice_lines_ignored': [
-                        ('add', x) for x in invoice_line_ids],
+                        ('add', x.id) for x in invoice_lines],
                     })
 
-Purchase()
-
 
 class PurchaseInvoiceLine(ModelSQL):
     'Purchase - Invoice Line'
-    _name = 'purchase.purchase-account.invoice.line'
+    __name__ = 'purchase.purchase-account.invoice.line'
     _table = 'purchase_invoice_line_rel'
-    _description = __doc__
     purchase = fields.Many2One('purchase.purchase', 'Purchase',
             ondelete='CASCADE', select=True, required=True)
     line = fields.Many2One('account.invoice.line', 'Invoice Line',
             ondelete='RESTRICT', select=True, required=True)
 
-PurchaseInvoiceLine()
-
 
 class PurchaseIgnoredInvoiceLine(ModelSQL):
     'Purchase - Ignored Invoice Line'
-    _name = 'purchase.purchase-ignored-account.invoice.line'
+    __name__ = 'purchase.purchase-ignored-account.invoice.line'
     _table = 'purchase_invoice_line_ignored_rel'
-    _description = __doc__
     purchase = fields.Many2One('purchase.purchase', 'Purchase',
             ondelete='CASCADE', select=True, required=True)
     invoice = fields.Many2One('account.invoice.line', 'Invoice Line',
             ondelete='RESTRICT', select=True, required=True)
-
-PurchaseIgnoredInvoiceLine()
diff -r 623b1e0a0127 trytond/modules/purchase_shipment_cost/__init__.py
--- a/trytond/modules/purchase_shipment_cost/__init__.py	Sat Sep 22 21:41:40 2012 +0200
+++ b/trytond/modules/purchase_shipment_cost/__init__.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,5 +1,14 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from stock import *
 from carrier import *
+
+
+def register():
+    Pool.register(
+        ShipmentIn,
+        Move,
+        Carrier,
+        module='purchase_shipment_cost', type_='model')
diff -r 623b1e0a0127 trytond/modules/purchase_shipment_cost/carrier.py
--- a/trytond/modules/purchase_shipment_cost/carrier.py	Sat Sep 22 21:41:40 2012 +0200
+++ b/trytond/modules/purchase_shipment_cost/carrier.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,16 +1,18 @@
 #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 Model, fields
+from trytond.model import fields
+from trytond.pool import PoolMeta
 
+__all__ = ['Carrier']
+__metaclass__ = PoolMeta
 
-class Carrier(Model):
-    _name = 'carrier'
 
+class Carrier:
+    __name__ = 'carrier'
     carrier_cost_allocation_method = fields.Selection([
             ('value', 'By Value'),
             ], 'Carrier Cost Allocation Method', required=True)
 
-    def default_carrier_cost_allocation_method(self):
+    @staticmethod
+    def default_carrier_cost_allocation_method():
         return 'value'
-
-Carrier()
diff -r 623b1e0a0127 trytond/modules/purchase_shipment_cost/stock.py
--- a/trytond/modules/purchase_shipment_cost/stock.py	Sat Sep 22 21:41:40 2012 +0200
+++ b/trytond/modules/purchase_shipment_cost/stock.py	Mon Sep 24 20:36:42 2012 +0200
@@ -6,13 +6,15 @@
 
 from trytond.model import Model, ModelView, Workflow, fields
 from trytond.pyson import Eval, Bool
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 
+__all__ = ['ShipmentIn', 'Move']
+__metaclass__ = PoolMeta
 
-class ShipmentIn(Model):
-    _name = 'stock.shipment.in'
 
+class ShipmentIn:
+    __name__ = 'stock.shipment.in'
     carrier = fields.Many2One('carrier', 'Carrier', states={
             'readonly': Eval('state') != 'draft',
             }, on_change=['carrier', 'incoming_moves'],
@@ -24,102 +26,78 @@
             }, depends=['cost', 'state'])
     cost_currency_digits = fields.Function(fields.Integer(
             'Cost Currency Digits', on_change_with=['currency']),
-        'get_cost_currency_digits')
+        'on_change_with_cost_currency_digits')
     cost = fields.Numeric('Cost', digits=(16, Eval('cost_currency_digits', 2)),
         states={
             'readonly': ~Eval('state').in_(['draft', 'assigned', 'packed']),
             }, depends=['carrier', 'state', 'cost_currency_digits'])
 
-    def __init__(self):
-        super(ShipmentIn, self).__init__()
-        self.incoming_moves = copy.copy(self.incoming_moves)
-        if not self.incoming_moves.on_change:
-            self.incoming_moves.on_change = []
-        else:
-            self.incoming_moves.on_change = copy.copy(
-                self.incoming_moves.on_change)
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentIn, cls).__setup__()
+        if not cls.incoming_moves.on_change:
+            cls.incoming_moves.on_change = []
         for fname in ('carrier', 'incoming_moves'):
-            if fname not in self.incoming_moves.on_change:
-                self.incoming_moves.on_change.append(fname)
-        self._rpc.setdefault('on_change_incoming_moves', False)
-        self.carrier = copy.copy(self.carrier)
-        self.carrier.on_change = copy.copy(self.carrier.on_change)
-        for fname in self.incoming_moves.on_change:
-            if fname not in self.carrier.on_change:
-                self.carrier.on_change.append(fname)
-        self._reset_columns()
+            if fname not in cls.incoming_moves.on_change:
+                cls.incoming_moves.on_change.append(fname)
+        for fname in cls.incoming_moves.on_change:
+            if fname not in cls.carrier.on_change:
+                cls.carrier.on_change.append(fname)
 
-    def on_change_with_cost_currency_digits(self, values):
-        currency_obj = Pool().get('currency.currency')
-        if values.get('cost_currency'):
-            currency = currency_obj.browse(values['cost_currency'])
-            return currency.digits
+    def on_change_with_cost_currency_digits(self, name=None):
+        if self.cost_currency:
+            return self.cost_currency.digits
         return 2
 
-    def get_cost_currency_digits(self, ids, name):
-        '''
-        Return the number of digits of the cost currency
-        '''
-        result = {}
-        for shipment in self.browse(ids):
-            if shipment.cost_currency:
-                result[shipment.id] = shipment.cost_currency.digits
-            else:
-                result[shipment.id] = 2
-        return result
-
-    def _get_carrier_context(self, values):
+    def _get_carrier_context(self):
         return {}
 
-    def on_change_carrier(self, values):
-        return self.on_change_incoming_moves(values)
+    def on_change_carrier(self):
+        return self.on_change_incoming_moves()
 
-    def on_change_incoming_moves(self, values):
-        pool = Pool()
-        carrier_obj = pool.get('carrier')
-        currency_obj = pool.get('currency.currency')
+    def on_change_incoming_moves(self):
+        Currency = Pool().get('currency.currency')
 
         try:
-            result = super(ShipmentIn, self).on_change_incoming_moves(values)
+            result = super(ShipmentIn, self).on_change_incoming_moves()
         except AttributeError:
             result = {}
-        if not values.get('carrier'):
+        if not self.carrier:
             return result
-        carrier = carrier_obj.browse(values['carrier'])
-        with Transaction().set_context(
-                self._get_carrier_context(values)):
-            cost, currency_id = carrier_obj.get_purchase_price(carrier)
+        with Transaction().set_context(self._get_carrier_context()):
+            cost, currency_id = self.carrier.get_purchase_price()
         result['cost'] = cost
         result['cost_currency'] = currency_id
         if currency_id:
-            currency = currency_obj.browse(currency_id)
+            currency = Currency(currency_id)
             result['cost_currency_digits'] = currency.digits
         else:
             result['cost_currency_digits'] = 2
         return result
 
-    def allocate_cost_by_value(self, shipment):
-        currency_obj = Pool().get('currency.currency')
-        move_obj = Pool().get('stock.move')
+    def allocate_cost_by_value(self):
+        pool = Pool()
+        Currency = pool.get('currency.currency')
+        Move = pool.get('stock.move')
 
-        if not shipment.cost:
+        if not self.cost:
             return
 
-        cost = currency_obj.compute(shipment.cost_currency, shipment.cost,
-            shipment.company.currency, round=False)
-        moves = [m for m in shipment.incoming_moves
+        cost = Currency.compute(self.cost_currency, self.cost,
+            self.company.currency, round=False)
+        moves = [m for m in self.incoming_moves
             if m.state not in ('done', 'cancel')]
 
         sum_value = 0
         unit_prices = {}
         for move in moves:
-            unit_price = currency_obj.compute(move.currency, move.unit_price,
-                shipment.company.currency, round=False)
+            unit_price = Currency.compute(move.currency, move.unit_price,
+                self.company.currency, round=False)
             unit_prices[move.id] = unit_price
             sum_value += unit_price * Decimal(str(move.quantity))
 
         costs = []
-        digit = move_obj.unit_price.digits[1]
+        digit = Move.unit_price.digits[1]
         exp = Decimal(str(10.0 ** -digit))
         difference = cost
         for move in moves:
@@ -145,66 +123,62 @@
 
         for cost in costs:
             move = cost['move']
-            unit_shipment_cost = currency_obj.compute(
-                shipment.company.currency, cost['unit_shipment_cost'],
+            unit_shipment_cost = Currency.compute(
+                self.company.currency, cost['unit_shipment_cost'],
                 move.currency, round=False)
             unit_shipment_cost = unit_shipment_cost.quantize(
                 exp, rounding=ROUND_HALF_EVEN)
-            move_obj.write(move.id, {
+            Move.write([move], {
                     'unit_price': move.unit_price + cost['unit_shipment_cost'],
                     'unit_shipment_cost': cost['unit_shipment_cost'],
                     })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('received')
-    def receive(self, ids):
-        carrier_obj = Pool().get('carrier')
-        for shipment in self.browse(ids):
+    def receive(cls, shipments):
+        Carrier = Pool().get('carrier')
+        for shipment in shipments:
             if shipment.carrier:
                 allocation_method = \
                     shipment.carrier.carrier_cost_allocation_method
             else:
                 allocation_method = \
-                    carrier_obj.default_carrier_cost_allocation_method()
-            getattr(self, 'allocate_cost_by_%s' % allocation_method)(shipment)
-        super(ShipmentIn, self).receive(ids)
+                    Carrier.default_carrier_cost_allocation_method()
+            getattr(shipment, 'allocate_cost_by_%s' % allocation_method)()
+        super(ShipmentIn, cls).receive(shipments)
 
-ShipmentIn()
 
-
-class Move(Model):
-    _name = 'stock.move'
-
+class Move:
+    __name__ = 'stock.move'
     unit_shipment_cost = fields.Numeric('Unit Shipment Cost', digits=(16, 4),
         readonly=True)
 
     # Split the shipment cost if account_stock_continental is installed
-    def _get_account_stock_move_lines(self, move, type_):
-        currency_obj = Pool().get('currency.currency')
-        move_lines = super(Move, self)._get_account_stock_move_lines(move,
-            type_)
+    def _get_account_stock_move_lines(self, type_):
+        pool = Pool()
+        AccountMoveLine = pool.get('account.move.line')
+        move_lines = super(Move, self)._get_account_stock_move_lines(type_)
         if (type_.startswith('in_')
-                and move.unit_shipment_cost
-                and move.shipment_in
-                and move.shipment_in.carrier):
-            shipment_cost = currency_obj.round(move.company.currency,
-                Decimal(str(move.quantity)) * move.unit_shipment_cost)
+                and self.unit_shipment_cost
+                and self.shipment_in
+                and self.shipment_in.carrier):
+            shipment_cost = self.company.currency.round(
+                Decimal(str(self.quantity)) * self.unit_shipment_cost)
             shipment_cost_account = \
-                move.shipment_in.carrier.carrier_product.account_expense_used
-            account_id = move.product.account_stock_supplier_used.id
+                self.shipment_in.carrier.carrier_product.account_expense_used
+            account = self.product.account_stock_supplier_used
             for move_line in move_lines:
-                if move_line['account'] == account_id:
-                    move_line['credit'] -= shipment_cost
-                    shipment_cost_line = {
-                        'name': move.rec_name,
-                        'debit': Decimal('0'),
-                        'credit': shipment_cost,
-                        'account': shipment_cost_account,
-                        }
+                if move_line.account == account:
+                    move_line.credit -= shipment_cost
+                    shipment_cost_line = AccountMoveLine(
+                        name=self.rec_name,
+                        debit=Decimal('0'),
+                        credit=shipment_cost,
+                        account=shipment_cost_account,
+                        )
                     move_lines.append(shipment_cost_line)
                     break
             else:
                 raise AssertionError('missing account_stock_supplier')
         return move_lines
-
-Move()
diff -r 623b1e0a0127 trytond/modules/purchase_shipment_cost/tests/scenario_purchase_shipment_cost_with_account_stock.rst
--- a/trytond/modules/purchase_shipment_cost/tests/scenario_purchase_shipment_cost_with_account_stock.rst	Sat Sep 22 21:41:40 2012 +0200
+++ b/trytond/modules/purchase_shipment_cost/tests/scenario_purchase_shipment_cost_with_account_stock.rst	Mon Sep 24 20:36:42 2012 +0200
@@ -145,7 +145,7 @@
     >>> product.account_journal_stock_customer = stock_journal
     >>> product.account_journal_stock_lost_found = stock_journal
     >>> product.save()
-    >>> product_average = Product(Product.copy(product.id, config.context))
+    >>> product_average = Product(Product.copy([product.id], config.context)[0])
     >>> product_average.cost_price_method = 'average'
     >>> product_average.save()
 
diff -r f3e24bd4d57e trytond/modules/sale/__init__.py
--- a/trytond/modules/sale/__init__.py	Tue Sep 11 15:23:08 2012 +0200
+++ b/trytond/modules/sale/__init__.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,6 +1,40 @@
 #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.pool import Pool
 from .sale import *
 from .configuration import *
 from .invoice import *
+
+
+def register():
+    Pool.register(
+        Sale,
+        SaleInvoice,
+        SaleIgnoredInvoice,
+        SaleRecreatedInvoice,
+        SaleLine,
+        SaleLineTax,
+        SaleLineInvoiceLine,
+        SaleLineIgnoredMove,
+        SaleLineRecreatedMove,
+        Template,
+        Product,
+        ShipmentOut,
+        ShipmentOutReturn,
+        Move,
+        HandleShipmentExceptionAsk,
+        HandleInvoiceExceptionAsk,
+        Configuration,
+        Invoice,
+        InvoiceLine,
+        module='sale', type_='model')
+    Pool.register(
+        OpenCustomer,
+        HandleShipmentException,
+        HandleInvoiceException,
+        ReturnSale,
+        module='sale', type_='wizard')
+    Pool.register(
+        SaleReport,
+        module='sale', type_='report')
diff -r f3e24bd4d57e trytond/modules/sale/configuration.py
--- a/trytond/modules/sale/configuration.py	Tue Sep 11 15:23:08 2012 +0200
+++ b/trytond/modules/sale/configuration.py	Mon Sep 24 20:36:42 2012 +0200
@@ -3,12 +3,12 @@
 from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
 from trytond.pyson import Eval, Bool
 
+__all__ = ['Configuration']
+
 
 class Configuration(ModelSingleton, ModelSQL, ModelView):
     'Sale Configuration'
-    _name = 'sale.configuration'
-    _description = __doc__
-
+    __name__ = 'sale.configuration'
     sale_sequence = fields.Property(fields.Many2One('ir.sequence',
             'Sale Reference Sequence', domain=[
                 ('company', 'in', [Eval('context', {}).get('company', 0),
@@ -29,5 +29,3 @@
                 ], 'Sale Shipment Method', states={
                 'required': Bool(Eval('context', {}).get('company', 0)),
                 }))
-
-Configuration()
diff -r f3e24bd4d57e trytond/modules/sale/invoice.py
--- a/trytond/modules/sale/invoice.py	Tue Sep 11 15:23:08 2012 +0200
+++ b/trytond/modules/sale/invoice.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,14 +1,51 @@
 #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 Model, fields
-from trytond.pool import Pool
+from trytond.model import Workflow, fields
+from trytond.pool import Pool, PoolMeta
+from trytond.transaction import Transaction
 
+__all__ = ['Invoice', 'InvoiceLine']
+__metaclass__ = PoolMeta
 
-class Invoice(Model):
-    _name = 'account.invoice'
 
+class Invoice:
+    __name__ = 'account.invoice'
     sales = fields.Many2Many('sale.sale-account.invoice',
             'invoice', 'sale', 'Sales', readonly=True)
+    sale_exception_state = fields.Function(fields.Selection([
+        ('', ''),
+        ('ignored', 'Ignored'),
+        ('recreated', 'Recreated'),
+        ], 'Exception State'), 'get_sale_exception_state')
+
+    @classmethod
+    def __setup__(cls):
+        super(Invoice, cls).__setup__()
+        cls._error_messages.update({
+            'delete_sale_invoice': 'You can not delete invoices '
+                    'that come from a sale!',
+            'reset_invoice_sale': 'You cannot reset to draft '
+                    'an invoice generated by a sale.',
+            })
+
+    @classmethod
+    def get_sale_exception_state(cls, invoices, name):
+        Sale = Pool().get('sale.sale')
+        sales = Sale.search([
+                ('invoices', 'in', [i.id for i in invoices]),
+                ])
+
+        recreated = tuple(i for p in sales for i in p.invoices_recreated)
+        ignored = tuple(i for p in sales for i in p.invoices_ignored)
+
+        states = {}
+        for invoice in invoices:
+            states[invoice.id] = ''
+            if invoice in recreated:
+                states[invoice.id] = 'recreated'
+            elif invoice.id in ignored:
+                states[invoice.id] = 'ignored'
+        return states
 
     def copy(self, ids, default=None):
         if default is None:
@@ -17,34 +54,53 @@
         default.setdefault('sales', None)
         return super(Invoice, self).copy(ids, default=default)
 
-    def paid(self, ids):
+    @classmethod
+    def delete(cls, invoices):
+        if invoices:
+            Transaction().cursor.execute('SELECT id FROM sale_invoices_rel '
+                'WHERE invoice IN (' + ','.join(('%s',) * len(invoices)) + ')',
+                [i.id for i in invoices])
+            if Transaction().cursor.fetchone():
+                cls.raise_user_error('delete_sale_invoice')
+        super(Invoice, cls).delete(invoices)
+
+    @classmethod
+    def paid(cls, invoices):
         pool = Pool()
-        sale_obj = pool.get('sale.sale')
-        super(Invoice, self).paid(ids)
-        invoices = self.browse(ids)
-        sale_obj.process([s.id for i in invoices for s in i.sales])
+        Sale = pool.get('sale.sale')
+        super(Invoice, cls).paid(invoices)
+        Sale.process([s for i in invoices for s in i.sales])
 
-    def cancel(self, ids):
+    @classmethod
+    def cancel(cls, invoices):
         pool = Pool()
-        sale_obj = pool.get('sale.sale')
-        super(Invoice, self).cancel(ids)
-        invoices = self.browse(ids)
-        sale_obj.process([s.id for i in invoices for s in i.sales])
+        Sale = pool.get('sale.sale')
+        super(Invoice, cls).cancel(invoices)
+        Sale.process([s for i in invoices for s in i.sales])
 
-Invoice()
+    @classmethod
+    @Workflow.transition('draft')
+    def draft(cls, invoices):
+        Sale = Pool().get('sale.sale')
+        sales = Sale.search([
+                ('invoices', 'in', [i.id for i in invoices]),
+                ])
 
+        if sales:
+            cls.raise_user_error('reset_invoice_sale')
 
-class InvoiceLine(Model):
-    _name = 'account.invoice.line'
+        return super(Invoice, cls).draft(invoices)
 
+
+class InvoiceLine:
+    __name__ = 'account.invoice.line'
     sale_lines = fields.Many2Many('sale.line-account.invoice.line',
             'invoice_line', 'sale_line', 'Sale Lines', readonly=True)
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, lines, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default.setdefault('sale_lines', None)
-        return super(InvoiceLine, self).copy(ids, default=default)
-
-InvoiceLine()
+        return super(InvoiceLine, cls).copy(lines, default=default)
diff -r f3e24bd4d57e trytond/modules/sale/sale.py
--- a/trytond/modules/sale/sale.py	Tue Sep 11 15:23:08 2012 +0200
+++ b/trytond/modules/sale/sale.py	Mon Sep 24 20:36:42 2012 +0200
@@ -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 copy
 from decimal import Decimal
 import datetime
 from itertools import groupby
@@ -12,16 +11,23 @@
 from trytond.backend import TableHandler
 from trytond.pyson import If, Eval, Bool, PYSONEncoder, Id
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.config import CONFIG
 
+__all__ = ['Sale', 'SaleInvoice', 'SaleIgnoredInvoice', 'SaleRecreatedInvoice',
+    'SaleLine', 'SaleLineTax', 'SaleLineInvoiceLine', 'SaleLineIgnoredMove',
+    'SaleLineRecreatedMove', 'SaleReport', 'Template', 'Product',
+    'ShipmentOut', 'ShipmentOutReturn', 'Move', 'OpenCustomer',
+    'HandleShipmentExceptionAsk', 'HandleShipmentException',
+    'HandleInvoiceExceptionAsk', 'HandleInvoiceException',
+    'ReturnSale']
+__metaclass__ = PoolMeta
+
 
 class Sale(Workflow, ModelSQL, ModelView):
     'Sale'
-    _name = 'sale.sale'
+    __name__ = 'sale.sale'
     _rec_name = 'reference'
-    _description = __doc__
-
     company = fields.Many2One('company.company', 'Company', required=True,
         states={
             'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
@@ -62,7 +68,7 @@
             }, on_change=['party', 'payment_term'],
         depends=['state'])
     party_lang = fields.Function(fields.Char('Party Language',
-        on_change_with=['party']), 'get_function_fields')
+            on_change_with=['party']), 'on_change_with_party_lang')
     invoice_address = fields.Many2One('party.address', 'Invoice Address',
         domain=[('party', '=', Eval('party'))], states={
             'readonly': Eval('state') != 'draft',
@@ -84,7 +90,7 @@
             },
         depends=['state'])
     currency_digits = fields.Function(fields.Integer('Currency Digits',
-        on_change_with=['currency']), 'get_function_fields')
+            on_change_with=['currency']), 'on_change_with_currency_digits')
     lines = fields.One2Many('sale.line', 'sale', 'Lines', states={
             'readonly': Eval('state') != 'draft',
             }, on_change=['lines', 'currency', 'party'],
@@ -154,26 +160,28 @@
         fields.One2Many('stock.shipment.out.return', None, 'Shipment Returns'),
         'get_shipment_returns')
     moves = fields.Function(fields.One2Many('stock.move', None, 'Moves'),
-            'get_function_fields')
+        'get_moves')
 
-    def __init__(self):
-        super(Sale, self).__init__()
-        self._order.insert(0, ('sale_date', 'DESC'))
-        self._order.insert(1, ('id', 'DESC'))
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Sale, cls).__setup__()
+        cls._order.insert(0, ('sale_date', 'DESC'))
+        cls._order.insert(1, ('id', 'DESC'))
+        cls._constraints += [
             ('check_method', 'wrong_method')
-        ]
-        self._error_messages.update({
-            'wrong_method': 'Wrong combination of method!',
-            'addresses_required': 'Invoice and Shipment addresses must be '
-            'defined for the quotation.',
-            'warehouse_required': 'Warehouse must be defined for the ' \
-                'quotation.',
-            'missing_account_receivable': 'It misses '
-                    'an "Account Receivable" on the party "%s"!',
-            'delete_cancel': 'Sale "%s" must be cancelled before deletion!',
-        })
-        self._transitions |= set((
+            ]
+        cls._error_messages.update({
+                'wrong_method': 'Wrong combination of method!',
+                'addresses_required': 'Invoice and Shipment addresses must be '
+                'defined for the quotation.',
+                'warehouse_required': 'Warehouse must be defined for the ' \
+                    'quotation.',
+                'missing_account_receivable': 'It misses '
+                'an "Account Receivable" on the party "%s"!',
+                'delete_cancel': 'Sale "%s" must be cancelled ' \
+                    'before deletion!',
+                })
+        cls._transitions |= set((
                 ('draft', 'quotation'),
                 ('quotation', 'confirmed'),
                 ('confirmed', 'processing'),
@@ -183,7 +191,7 @@
                 ('quotation', 'draft'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': ~Eval('state').in_(['draft', 'quotation']),
                     },
@@ -216,13 +224,14 @@
                     },
                 })
         # The states where amounts are cached
-        self._states_cached = ['confirmed', 'processing', 'done', 'cancel']
+        cls._states_cached = ['confirmed', 'processing', 'done', 'cancel']
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         pool = Pool()
-        sale_line_obj = pool.get('sale.line')
-        sale_line_invoice_line_obj = pool.get('sale.line-account.invoice.line')
-        move_obj = pool.get('stock.move')
+        SaleLine = pool.get('sale.line')
+        SaleLine_InvoiceLine = pool.get('sale.line-account.invoice.line')
+        Move = pool.get('stock.move')
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         cursor.execute("UPDATE ir_model_data "
@@ -235,27 +244,27 @@
                 "WHERE (relation like '%%packing%%' "
                     "OR name like '%%packing%%') AND module = %s",
                 (module_name,))
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('packing_state', 'shipment_state')
         table.column_rename('packing_method', 'shipment_method')
         table.column_rename('packing_address', 'shipment_address')
 
-        super(Sale, self).init(module_name)
+        super(Sale, cls).__register__(module_name)
 
         # Migration from 1.2
-        cursor.execute("UPDATE " + self._table + " "
+        cursor.execute("UPDATE " + cls._table + " "
                 "SET invoice_method = 'shipment' "
                 "WHERE invoice_method = 'packing'")
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         # Migration from 2.2
         table.not_null_action('sale_date', 'remove')
 
         # state confirmed splitted into confirmed and processing
-        if (TableHandler.table_exist(cursor, sale_line_obj._table)
+        if (TableHandler.table_exist(cursor, SaleLine._table)
                 and TableHandler.table_exist(cursor,
-                    sale_line_invoice_line_obj._table)
-                and TableHandler.table_exist(cursor, move_obj._table)):
+                    SaleLine_InvoiceLine._table)
+                and TableHandler.table_exist(cursor, Move._table)):
             # Wrap subquery inside an other inner subquery because MySQL syntax
             # doesn't allow update a table and select from the same table in a
             # subquery.
@@ -272,290 +281,201 @@
                         "WHERE s.state = 'confirmed' "
                             'AND (li.id IS NOT NULL '
                                 'OR m.id IS NOT NULL)) AS foo)'
-                % (self._table, self._table, sale_line_obj._table,
-                    sale_line_invoice_line_obj._table, move_obj._table))
+                % (cls._table, cls._table, SaleLine._table,
+                    SaleLine_InvoiceLine._table, Move._table))
 
         # Add index on create_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action('create_date', action='add')
 
-    def default_payment_term(self):
-        payment_term_obj = Pool().get('account.invoice.payment_term')
-        payment_term_ids = payment_term_obj.search(self.payment_term.domain)
-        if len(payment_term_ids) == 1:
-            return payment_term_ids[0]
+    @classmethod
+    def default_payment_term(cls):
+        PaymentTerm = Pool().get('account.invoice.payment_term')
+        payment_terms = PaymentTerm.search(cls.payment_term.domain)
+        if len(payment_terms) == 1:
+            return payment_terms[0].id
 
-    def default_warehouse(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(self.warehouse.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_warehouse(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.warehouse.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_currency(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency():
+        Company = Pool().get('company.company')
         company = Transaction().context.get('company')
         if company:
-            return company_obj.browse(company).currency.id
+            return Company(company).currency.id
 
-    def default_currency_digits(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency_digits():
+        Company = Pool().get('company.company')
         company = Transaction().context.get('company')
         if company:
-            return company_obj.browse(company).currency.digits
+            return Company(company).currency.digits
         return 2
 
-    def default_invoice_method(self):
-        config_obj = Pool().get('sale.configuration')
-        config = config_obj.browse(1)
+    @staticmethod
+    def default_invoice_method():
+        Config = Pool().get('sale.configuration')
+        config = Config(1)
         return config.sale_invoice_method
 
-    def default_invoice_state(self):
+    @staticmethod
+    def default_invoice_state():
         return 'none'
 
-    def default_shipment_method(self):
-        config_obj = Pool().get('sale.configuration')
-        config = config_obj.browse(1)
+    @staticmethod
+    def default_shipment_method():
+        Config = Pool().get('sale.configuration')
+        config = Config(1)
         return config.sale_shipment_method
 
-    def default_shipment_state(self):
+    @staticmethod
+    def default_shipment_state():
         return 'none'
 
-    def on_change_party(self, vals):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        address_obj = pool.get('party.address')
-        payment_term_obj = pool.get('account.invoice.payment_term')
-        res = {
-            'invoice_address': None,
-            'shipment_address': None,
-            'payment_term': None,
-        }
-        if vals.get('party'):
-            party = party_obj.browse(vals['party'])
-            res['invoice_address'] = party_obj.address_get(party.id,
-                    type='invoice')
-            res['shipment_address'] = party_obj.address_get(party.id,
-                    type='delivery')
-            if party.customer_payment_term:
-                res['payment_term'] = party.customer_payment_term.id
+    def on_change_party(self):
+        invoice_address = None
+        shipment_address = None
+        payment_term = None
+        if self.party:
+            invoice_address = self.party.address_get(type='invoice')
+            shipment_address = self.party.address_get(type='delivery')
+            if self.party.customer_payment_term:
+                payment_term = self.party.customer_payment_term
 
-        if res['invoice_address']:
-            res['invoice_address.rec_name'] = address_obj.browse(
-                    res['invoice_address']).rec_name
-        if res['shipment_address']:
-            res['shipment_address.rec_name'] = address_obj.browse(
-                    res['shipment_address']).rec_name
-        if not res['payment_term']:
-            res['payment_term'] = self.default_payment_term()
-        if res['payment_term']:
-            res['payment_term.rec_name'] = payment_term_obj.browse(
-                    res['payment_term']).rec_name
+        changes = {}
+        if invoice_address:
+            changes['invoice_address'] = invoice_address.id
+            changes['invoice_address.rec_name'] = invoice_address.rec_name
+        else:
+            changes['invoice_address'] = None
+        if shipment_address:
+            changes['shipment_address'] = shipment_address.id
+            changes['shipment_address.rec_name'] = shipment_address.rec_name
+        else:
+            changes['shipment_address'] = None
+        if payment_term:
+            changes['payment_term'] = payment_term.id
+            changes['payment_term.rec_name'] = payment_term.rec_name
+        else:
+            changes['payment_term'] = self.default_payment_term()
+        return changes
+
+    def on_change_with_currency_digits(self, name=None):
+        if self.currency:
+            return self.currency.digits
+        return 2
+
+    def get_tax_context(self):
+        res = {}
+        if self.party and self.party.lang:
+            res['language'] = self.party.lang.code
         return res
 
-    def on_change_with_currency_digits(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-            return currency.digits
-        return 2
-
-    def get_currency_digits(self, sales):
-        '''
-        Return the number of digits of the currency of each sales
-
-        :param sales: a BrowseRecordList of puchases
-        :return: a dictionary with sale id as key and
-            number of digits as value
-        '''
-        res = {}
-        for sale in sales:
-            res[sale.id] = sale.currency.digits
-        return res
-
-    def get_tax_context(self, sale):
-        party_obj = Pool().get('party.party')
-        res = {}
-        if isinstance(sale, dict):
-            if sale.get('party'):
-                party = party_obj.browse(sale['party'])
-                if party.lang:
-                    res['language'] = party.lang.code
-        else:
-            if sale.party.lang:
-                res['language'] = sale.party.lang.code
-        return res
-
-    def on_change_with_party_lang(self, vals):
-        party_obj = Pool().get('party.party')
-        if vals.get('party'):
-            party = party_obj.browse(vals['party'])
-            if party.lang:
-                return party.lang.code
+    def on_change_with_party_lang(self, name=None):
+        if self.party and self.party.lang:
+            return self.party.lang.code
         return CONFIG['language']
 
-    def get_party_lang(self, sales):
-        '''
-        Return the code lang of the party for each sales
-
-        :param sales: a BrowseRecordList of sales
-        :return: a dictionary with sale id as key and
-            code lang as value
-        '''
-        res = {}
-        for sale in sales:
-            if sale.party.lang:
-                res[sale.id] = sale.party.lang.code
-            else:
-                res[sale.id] = CONFIG['language']
-        return res
-
-    def on_change_lines(self, vals):
+    def on_change_lines(self):
         pool = Pool()
-        currency_obj = pool.get('currency.currency')
-        tax_obj = pool.get('account.tax')
-        invoice_obj = pool.get('account.invoice')
+        Tax = pool.get('account.tax')
+        Invoice = pool.get('account.invoice')
 
         res = {
             'untaxed_amount': Decimal('0.0'),
             'tax_amount': Decimal('0.0'),
             'total_amount': Decimal('0.0'),
-        }
+            }
 
-        currency = None
-        if vals.get('currency'):
-            currency = currency_obj.browse(vals['currency'])
-
-        if vals.get('lines'):
+        if self.lines:
             taxes = {}
-            for line in vals['lines']:
-                if line.get('type', 'line') != 'line':
+            for line in self.lines:
+                if getattr(line, 'type', 'line') != 'line':
                     continue
-                res['untaxed_amount'] += line.get('amount') or Decimal(0)
+                res['untaxed_amount'] += line.amount or Decimal(0)
                 tax_list = ()
-                with Transaction().set_context(self.get_tax_context(vals)):
-                    tax_list = tax_obj.compute(line.get('taxes', []),
-                            line.get('unit_price') or Decimal('0.0'),
-                            line.get('quantity') or 0.0)
+                with Transaction().set_context(self.get_tax_context()):
+                    tax_list = Tax.compute(getattr(line, 'taxes', []),
+                        line.unit_price or Decimal('0.0'),
+                        line.quantity or 0.0)
                 for tax in tax_list:
-                    key, val = invoice_obj._compute_tax(tax, 'out_invoice')
+                    key, val = Invoice._compute_tax(tax, 'out_invoice')
                     if not key in taxes:
                         taxes[key] = val['amount']
                     else:
                         taxes[key] += val['amount']
-            if currency:
+            if self.currency:
                 for key in taxes:
-                    res['tax_amount'] += currency_obj.round(currency,
-                            taxes[key])
-        if currency:
-            res['untaxed_amount'] = currency_obj.round(currency,
-                    res['untaxed_amount'])
-            res['tax_amount'] = currency_obj.round(currency,
-                    res['tax_amount'])
+                    res['tax_amount'] += self.currency.round(taxes[key])
+        if self.currency:
+            res['untaxed_amount'] = self.currency.round(res['untaxed_amount'])
+            res['tax_amount'] = self.currency.round(res['tax_amount'])
         res['total_amount'] = res['untaxed_amount'] + res['tax_amount']
-        if currency:
-            res['total_amount'] = currency_obj.round(currency,
-                    res['total_amount'])
+        if self.currency:
+            res['total_amount'] = self.currency.round(res['total_amount'])
         return res
 
-    def get_function_fields(self, ids, names):
-        '''
-        Function to compute function fields for sale ids
+    def get_untaxed_amount(self, name):
+        if (self.state in self._states_cached
+                and self.untaxed_amount_cache is not None):
+            return self.untaxed_amount_cache
+        amount = sum((l.amount for l in self.lines if l.type == 'line'),
+            Decimal(0))
+        return self.currency.round(amount)
 
-        :param ids: the ids of the sales
-        :param names: the list of field name to compute
-        :return: a dictionary with all field names as key and
-            a dictionary as value with id as key
-        '''
-        res = {}
-        sales = self.browse(ids)
-        if 'currency_digits' in names:
-            res['currency_digits'] = self.get_currency_digits(sales)
-        if 'party_lang' in names:
-            res['party_lang'] = self.get_party_lang(sales)
-        if 'moves' in names:
-            res['moves'] = self.get_moves(sales)
-        return res
+    def get_tax_amount(self, name):
+        pool = Pool()
+        Tax = pool.get('account.tax')
+        Invoice = pool.get('account.invoice')
 
-    def get_untaxed_amount(self, ids, name):
-        '''
-        Compute the untaxed amount for each sales
-        '''
-        currency_obj = Pool().get('currency.currency')
-        amounts = {}
-        for sale in self.browse(ids):
-            if (sale.state in self._states_cached
-                    and sale.untaxed_amount_cache is not None):
-                amounts[sale.id] = sale.untaxed_amount_cache
+        if (self.state in self._states_cached
+                and self.tax_amount_cache is not None):
+            return self.tax_amount_cache
+        context = self.get_tax_context()
+        taxes = {}
+        for line in self.lines:
+            if line.type != 'line':
                 continue
-            amount = sum((l.amount for l in sale.lines if l.type == 'line'),
-                Decimal(0))
-            amounts[sale.id] = currency_obj.round(sale.currency, amount)
-        return amounts
+            with Transaction().set_context(context):
+                tax_list = Tax.compute(line.taxes, line.unit_price,
+                    line.quantity)
+            # Don't round on each line to handle rounding error
+            for tax in tax_list:
+                key, val = Invoice._compute_tax(tax, 'out_invoice')
+                if not key in taxes:
+                    taxes[key] = val['amount']
+                else:
+                    taxes[key] += val['amount']
+        amount = sum((self.currency.round(taxes[key]) for key in taxes),
+            Decimal(0))
+        return self.currency.round(amount)
 
-    def get_tax_amount(self, ids, name):
-        '''
-        Compute tax amount for each sales
-        '''
-        pool = Pool()
-        currency_obj = pool.get('currency.currency')
-        tax_obj = pool.get('account.tax')
-        invoice_obj = pool.get('account.invoice')
+    def get_total_amount(self, name):
+        if (self.state in self._states_cached
+                and self.total_amount_cache is not None):
+            return self.total_amount_cache
+        return self.currency.round(self.untaxed_amount + self.tax_amount)
 
-        amounts = {}
-        for sale in self.browse(ids):
-            if (sale.state in self._states_cached
-                    and sale.tax_amount_cache is not None):
-                amounts[sale.id] = sale.tax_amount_cache
-                continue
-            context = self.get_tax_context(sale)
-            taxes = {}
-            for line in sale.lines:
-                if line.type != 'line':
-                    continue
-                with Transaction().set_context(context):
-                    tax_list = tax_obj.compute(
-                            [t.id for t in line.taxes], line.unit_price,
-                            line.quantity)
-                # Don't round on each line to handle rounding error
-                for tax in tax_list:
-                    key, val = invoice_obj._compute_tax(tax, 'out_invoice')
-                    if not key in taxes:
-                        taxes[key] = val['amount']
-                    else:
-                        taxes[key] += val['amount']
-            amount = sum((currency_obj.round(sale.currency, taxes[key])
-                    for key in taxes), Decimal(0))
-            amounts[sale.id] = currency_obj.round(sale.currency, amount)
-        return amounts
-
-    def get_total_amount(self, ids, name):
-        '''
-        Return the total amount of each sales
-        '''
-        currency_obj = Pool().get('currency.currency')
-        amounts = {}
-        for sale in self.browse(ids):
-            if (sale.state in self._states_cached
-                    and sale.total_amount_cache is not None):
-                amounts[sale.id] = sale.total_amount_cache
-                continue
-            amounts[sale.id] = currency_obj.round(sale.currency,
-                sale.untaxed_amount + sale.tax_amount)
-        return amounts
-
-    def get_invoice_state(self, sale):
+    def get_invoice_state(self):
         '''
         Return the invoice state for the sale.
         '''
-        skip_ids = set(x.id for x in sale.invoices_ignored)
-        skip_ids.update(x.id for x in sale.invoices_recreated)
-        invoices = [i for i in sale.invoices if i.id not in skip_ids]
+        skip_ids = set(x.id for x in self.invoices_ignored)
+        skip_ids.update(x.id for x in self.invoices_recreated)
+        invoices = [i for i in self.invoices if i.id not in skip_ids]
         if invoices:
             if any(i.state == 'cancel' for i in invoices):
                 return 'exception'
@@ -565,102 +485,84 @@
                 return 'waiting'
         return 'none'
 
-    def set_invoice_state(self, sale):
+    def set_invoice_state(self):
         '''
         Set the invoice state.
         '''
-        state = self.get_invoice_state(sale)
-        if sale.invoice_state != state:
-            self.write(sale.id, {
+        state = self.get_invoice_state()
+        if self.invoice_state != state:
+            self.write([self], {
                     'invoice_state': state,
                     })
 
     def get_shipments_returns(attribute):
         "Computes the returns or shipments"
-        def method(self, ids, name):
-            shipments = {}
-            for sale in self.browse(ids):
-                shipments[sale.id] = []
-                for line in sale.lines:
-                    for move in line.moves:
-                        ship_or_return = getattr(move, attribute)
-                        if bool(ship_or_return):
-                            if ship_or_return.id not in shipments[sale.id]:
-                                shipments[sale.id].append(ship_or_return.id)
+        def method(self, name):
+            shipments = []
+            for line in self.lines:
+                for move in line.moves:
+                    ship_or_return = getattr(move, attribute)
+                    if bool(ship_or_return):
+                        if ship_or_return.id not in shipments:
+                            shipments.append(ship_or_return.id)
             return shipments
         return method
 
     get_shipments = get_shipments_returns('shipment_out')
     get_shipment_returns = get_shipments_returns('shipment_out_return')
 
-    def get_moves(self, sales):
-        '''
-        Return move ids for each sales
+    def get_moves(self, name):
+        return [m.id for l in self.lines for m in l.moves]
 
-        :param sales: a BrowseRecordList of sales
-        :return: a dictionary with sale id as key and
-            a list of move ids as value
-        '''
-        res = {}
-        for sale in sales:
-            res[sale.id] = []
-            for line in sale.lines:
-                res[sale.id].extend([x.id for x in line.moves])
-        return res
-
-    def get_shipment_state(self, sale):
+    def get_shipment_state(self):
         '''
         Return the shipment state for the sale.
         '''
-        if sale.moves:
-            if any(l.move_exception for l in sale.lines):
+        if self.moves:
+            if any(l.move_exception for l in self.lines):
                 return 'exception'
-            elif all(l.move_done for l in sale.lines):
+            elif all(l.move_done for l in self.lines):
                 return 'sent'
             else:
                 return 'waiting'
         return 'none'
 
-    def set_shipment_state(self, sale):
+    def set_shipment_state(self):
         '''
         Set the shipment state.
         '''
-        state = self.get_shipment_state(sale)
-        if sale.shipment_state != state:
-            self.write(sale.id, {
+        state = self.get_shipment_state()
+        if self.shipment_state != state:
+            self.write([self], {
                     'shipment_state': state,
                     })
 
-    def check_method(self, ids):
+    def check_method(self):
         '''
         Check the methods.
         '''
-        for sale in self.browse(ids):
-            if sale.invoice_method == 'shipment' \
-                    and sale.shipment_method in ('invoice', 'manual'):
-                return False
-            if sale.shipment_method == 'invoice' \
-                    and sale.invoice_method in ('shipment', 'manual'):
-                return False
+        if (self.invoice_method == 'shipment'
+                and self.shipment_method in ('invoice', 'manual')):
+            return False
+        if (self.shipment_method == 'invoice'
+                and self.invoice_method in ('shipment', 'manual')):
+            return False
         return True
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return []
-        res = {}
-        for sale in self.browse(ids):
-            res[sale.id] = sale.reference or str(sale.id) \
-                    + ' - ' + sale.party.rec_name
-        return res
+    def get_rec_name(self, name):
+        return (self.reference or str(self.id)
+            + ' - ' + self.party.rec_name)
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         names = clause[2].split(' - ', 1)
         res = [('reference', clause[1], names[0])]
         if len(names) != 1 and names[1]:
             res.append(('party', clause[1], names[1]))
         return res
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, sales, default=None):
         if default is None:
             default = {}
         default = default.copy()
@@ -671,146 +573,143 @@
         default['invoices_ignored'] = None
         default['shipment_state'] = 'none'
         default.setdefault('sale_date', None)
-        return super(Sale, self).copy(ids, default=default)
+        return super(Sale, cls).copy(sales, default=default)
 
-    def check_for_quotation(self, ids):
-        sales = self.browse(ids)
-        for sale in sales:
-            if not sale.invoice_address or not sale.shipment_address:
-                self.raise_user_error('addresses_required')
-            for line in sale.lines:
-                if line.quantity >= 0:
-                    location = line.from_location
-                else:
-                    location = line.to_location
-                if ((not location or not line.warehouse)
-                        and line.product
-                        and line.product.type in ('goods', 'assets')):
-                    self.raise_user_error('warehouse_required')
+    def check_for_quotation(self):
+        if not self.invoice_address or not self.shipment_address:
+            self.raise_user_error('addresses_required')
+        for line in self.lines:
+            if line.quantity >= 0:
+                location = line.from_location
+            else:
+                location = line.to_location
+            if ((not location or not line.warehouse)
+                    and line.product
+                    and line.product.type in ('goods', 'assets')):
+                self.raise_user_error('warehouse_required')
 
-    def set_reference(self, ids):
+    @classmethod
+    def set_reference(cls, sales):
         '''
         Fill the reference field with the sale sequence
         '''
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('sale.configuration')
+        pool = Pool()
+        Sequence = pool.get('ir.sequence')
+        Config = pool.get('sale.configuration')
 
-        config = config_obj.browse(1)
-        sales = self.browse(ids)
+        config = Config(1)
         for sale in sales:
             if sale.reference:
                 continue
-            reference = sequence_obj.get_id(config.sale_sequence.id)
-            self.write(sale.id, {
-                'reference': reference,
-                })
+            reference = Sequence.get_id(config.sale_sequence.id)
+            cls.write([sale], {
+                    'reference': reference,
+                    })
 
-    def set_sale_date(self, ids):
-        date_obj = Pool().get('ir.date')
-        for sale in self.browse(ids):
+    @classmethod
+    def set_sale_date(cls, sales):
+        Date = Pool().get('ir.date')
+        for sale in sales:
             if not sale.sale_date:
-                self.write(sale.id, {
-                        'sale_date': date_obj.today(),
+                cls.write([sale], {
+                        'sale_date': Date.today(),
                         })
 
-    def store_cache(self, ids):
-        for sale in self.browse(ids):
-            self.write(sale.id, {
+    @classmethod
+    def store_cache(cls, sales):
+        for sale in sales:
+            cls.write([sale], {
                     'untaxed_amount_cache': sale.untaxed_amount,
                     'tax_amount_cache': sale.tax_amount,
                     'total_amount_cache': sale.total_amount,
                     })
 
-    def _get_invoice_line_sale_line(self, sale, invoice_type):
+    def _get_invoice_line_sale_line(self, invoice_type):
         '''
-        Return invoice line values for each sale lines according to
-        invoice_type
+        Return invoice line for each sale lines according to invoice_type
         '''
-        line_obj = Pool().get('sale.line')
         res = {}
-        for line in sale.lines:
-            val = line_obj.get_invoice_line(line, invoice_type)
+        for line in self.lines:
+            val = line.get_invoice_line(invoice_type)
             if val:
                 res[line.id] = val
         return res
 
-    def _get_invoice_sale(self, sale, invoice_type):
+    def _get_invoice_sale(self, invoice_type):
         '''
-        Return invoice values of type invoice_type for sale
-        '''
-        journal_obj = Pool().get('account.journal')
-
-        journal_id = journal_obj.search([
-            ('type', '=', 'revenue'),
-            ], limit=1)
-        if journal_id:
-            journal_id = journal_id[0]
-
-        res = {
-            'company': sale.company.id,
-            'type': invoice_type,
-            'reference': sale.reference,
-            'journal': journal_id,
-            'party': sale.party.id,
-            'invoice_address': sale.invoice_address.id,
-            'currency': sale.currency.id,
-            'account': sale.party.account_receivable.id,
-            'payment_term': sale.payment_term.id,
-            }
-        return res
-
-    def create_invoice(self, sale, invoice_type):
-        '''
-        Create an invoice of type invoice_type for the sale and return the id
+        Return invoice of type invoice_type
         '''
         pool = Pool()
-        invoice_obj = pool.get('account.invoice')
-        invoice_line_obj = pool.get('account.invoice.line')
-        sale_line_obj = pool.get('sale.line')
+        Invoice = pool.get('account.invoice')
+        Journal = pool.get('account.journal')
 
-        if sale.invoice_method == 'manual':
+        journals = Journal.search([
+                ('type', '=', 'revenue'),
+                ], limit=1)
+        if journals:
+            journal, = journals
+        else:
+            journal = None
+
+        with Transaction().set_user(0, set_context=True):
+            return Invoice(
+                company=self.company,
+                type=invoice_type,
+                reference=self.reference,
+                journal=journal,
+                party=self.party,
+                invoice_address=self.invoice_address,
+                currency=self.currency,
+                account=self.party.account_receivable,
+                payment_term=self.payment_term,
+                )
+
+    def create_invoice(self, invoice_type):
+        '''
+        Create and return an invoice of type invoice_type
+        '''
+        pool = Pool()
+        Invoice = pool.get('account.invoice')
+        SaleLine = pool.get('sale.line')
+        if self.invoice_method == 'manual':
             return
 
-        if not sale.party.account_receivable:
+        if not self.party.account_receivable:
             self.raise_user_error('missing_account_receivable',
-                    error_args=(sale.party.rec_name,))
+                    error_args=(self.party.rec_name,))
 
-        invoice_lines = self._get_invoice_line_sale_line(sale, invoice_type)
+        invoice_lines = self._get_invoice_line_sale_line(invoice_type)
         if not invoice_lines:
             return
 
-        vals = self._get_invoice_sale(sale, invoice_type)
-        with Transaction().set_user(0, set_context=True):
-            invoice_id = invoice_obj.create(vals)
+        invoice = self._get_invoice_sale(invoice_type)
+        invoice.save()
 
-        for line in sale.lines:
+        for line in self.lines:
             if line.id not in invoice_lines:
                 continue
-            for vals in invoice_lines[line.id]:
-                vals['invoice'] = invoice_id
-                with Transaction().set_user(0, set_context=True):
-                    invoice_line_id = invoice_line_obj.create(vals)
-                sale_line_obj.write(line.id, {
-                    'invoice_lines': [('add', invoice_line_id)],
-                    })
+            for invoice_line in invoice_lines[line.id]:
+                invoice_line.invoice = invoice.id
+                invoice_line.save()
+                SaleLine.write([line], {
+                        'invoice_lines': [('add', [invoice_line.id])],
+                        })
 
         with Transaction().set_user(0, set_context=True):
-            invoice_obj.update_taxes([invoice_id])
+            Invoice.update_taxes([invoice])
 
-        self.write(sale.id, {
-            'invoices': [('add', invoice_id)],
-        })
-        return invoice_id
+        self.write([self], {
+                'invoices': [('add', [invoice.id])],
+                })
+        return invoice
 
-    def _get_move_sale_line(self, sale, shipment_type):
+    def _get_move_sale_line(self, shipment_type):
         '''
-        Return a dictionary of move values for each sale lines of the right
-        shipment_type
+        Return move for each sale lines of the right shipment_type
         '''
-        line_obj = Pool().get('sale.line')
         res = {}
-        for line in sale.lines:
-            val = line_obj.get_move(line, shipment_type)
+        for line in self.lines:
+            val = line.get_move(shipment_type)
             if val:
                 res[line.id] = val
         return res
@@ -819,16 +718,13 @@
         '''
         The key to group moves by shipments
 
-        :param moves: a list of moves values
-        :param move: a tuple of line id and a dictionary of the move values
+        move is a tuple of line id and a move
+        '''
+        SaleLine = Pool().get('sale.line')
+        line_id, move = move
+        line = SaleLine(line_id)
 
-        :return: a list of key-value as tuples of the shipment
-        '''
-        sale_line_obj = Pool().get('sale.line')
-        line_id, move = move
-        line = sale_line_obj.browse(line_id)
-
-        planned_date = max(m['planned_date'] for m in moves)
+        planned_date = max(m.planned_date for m in moves)
         return (
             ('planned_date', planned_date),
             ('warehouse', line.warehouse.id),
@@ -836,168 +732,161 @@
 
     _group_return_key = _group_shipment_key
 
-    def create_shipment(self, sale, shipment_type):
+    def create_shipment(self, shipment_type):
         '''
-        Create a shipment of type shipment_type for the sale
-        and return the list of created shipment ids
+        Create and return shipments of type shipment_type
         '''
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        sale_line_obj = pool.get('sale.line')
+        SaleLine = pool.get('sale.line')
 
-        if sale.shipment_method == 'manual':
+        if self.shipment_method == 'manual':
             return
 
-        moves = self._get_move_sale_line(sale, shipment_type)
+        moves = self._get_move_sale_line(shipment_type)
         if not moves:
             return
         if shipment_type == 'out':
             keyfunc = partial(self._group_shipment_key, moves.values())
             move_shipment_key = 'shipment_out'
-            shipment_obj = pool.get('stock.shipment.out')
+            Shipment = pool.get('stock.shipment.out')
         elif shipment_type == 'return':
             keyfunc = partial(self._group_return_key, moves.values())
             move_shipment_key = 'shipment_out_return'
-            shipment_obj = pool.get('stock.shipment.out.return')
+            Shipment = pool.get('stock.shipment.out.return')
         moves = moves.items()
         moves = sorted(moves, key=keyfunc)
 
         shipments = []
-        with Transaction().set_user(0, set_context=True):
-            for key, grouped_moves in groupby(moves, key=keyfunc):
-                values = {
-                    'customer': sale.party.id,
-                    'delivery_address': sale.shipment_address.id,
-                    'reference': sale.reference,
-                    'company': sale.company.id,
-                    }
-                values.update(dict(key))
-                shipment_id = shipment_obj.create(values)
-                shipments.append(shipment_id)
-                for line_id, values in grouped_moves:
-                    values[move_shipment_key] = shipment_id
-                    move_id = move_obj.create(values)
-                    sale_line_obj.write(line_id, {
-                        'moves': [('add', move_id)],
-                    })
-            if shipment_type == 'out':
-                shipment_obj.wait(shipments)
+        for key, grouped_moves in groupby(moves, key=keyfunc):
+            values = {
+                'customer': self.party.id,
+                'delivery_address': self.shipment_address.id,
+                'reference': self.reference,
+                'company': self.company.id,
+                }
+            values.update(dict(key))
+            with Transaction().set_user(0, set_context=True):
+                shipment = Shipment.create(values)
+                shipments.append(shipment)
+                for line_id, move in grouped_moves:
+                    setattr(move, move_shipment_key, shipment.id)
+                    move.save()
+                    SaleLine.write([SaleLine(line_id)], {
+                            'moves': [('add', [move.id])],
+                            })
+        if shipment_type == 'out':
+            with Transaction().set_user(0, set_context=True):
+                Shipment.wait(shipments)
         return shipments
 
-    def is_done(self, sale):
-        return ((sale.invoice_state == 'paid'
-                or sale.invoice_method == 'manual')
-            and (sale.shipment_state == 'sent'
-                or sale.shipment_method == 'manual'))
+    def is_done(self):
+        return ((self.invoice_state == 'paid'
+                or self.invoice_method == 'manual')
+            and (self.shipment_state == 'sent'
+                or self.shipment_method == 'manual'))
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, sales):
         # Cancel before delete
-        self.cancel(ids)
-        for sale in self.browse(ids):
+        cls.cancel(sales)
+        for sale in sales:
             if sale.state != 'cancel':
-                self.raise_user_error('delete_cancel', sale.rec_name)
-        return super(Sale, self).delete(ids)
+                cls.raise_user_error('delete_cancel', sale.rec_name)
+        super(Sale, cls).delete(sales)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        self.store_cache(ids)
+    def cancel(cls, sales):
+        cls.store_cache(sales)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
+    def draft(cls, sales):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('quotation')
-    def quote(self, ids):
-        self.check_for_quotation(ids)
-        self.set_reference(ids)
+    def quote(cls, sales):
+        for sale in sales:
+            sale.check_for_quotation()
+        cls.set_reference(sales)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('confirmed')
-    def confirm(self, ids):
-        self.set_sale_date(ids)
-        self.store_cache(ids)
+    def confirm(cls, sales):
+        cls.set_sale_date(sales)
+        cls.store_cache(sales)
 
+    @classmethod
     @ModelView.button_action('sale.wizard_invoice_handle_exception')
-    def handle_invoice_exception(self, ids):
+    def handle_invoice_exception(cls, sales):
         pass
 
+    @classmethod
     @ModelView.button_action('sale.wizard_shipment_handle_exception')
-    def handle_shipment_exception(self, ids):
+    def handle_shipment_exception(cls, sales):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('processing')
-    def process(self, ids):
+    def process(cls, sales):
         done = []
-        for sale in self.browse(ids):
+        for sale in sales:
             if sale.state in ('done', 'cancel'):
                 continue
-            self.create_invoice(sale, 'out_invoice')
-            self.create_invoice(sale, 'out_credit_note')
-            self.set_invoice_state(sale)
-            self.create_shipment(sale, 'out')
-            self.create_shipment(sale, 'return')
-            self.set_shipment_state(sale)
-            if self.is_done(sale):
-                done.append(sale.id)
+            sale.create_invoice('out_invoice')
+            sale.create_invoice('out_credit_note')
+            sale.set_invoice_state()
+            sale.create_shipment('out')
+            sale.create_shipment('return')
+            sale.set_shipment_state()
+            if sale.is_done():
+                done.append(sale)
         if done:
-            self.write(done, {
+            cls.write(done, {
                     'state': 'done',
                     })
 
-Sale()
-
 
 class SaleInvoice(ModelSQL):
     'Sale - Invoice'
-    _name = 'sale.sale-account.invoice'
+    __name__ = 'sale.sale-account.invoice'
     _table = 'sale_invoices_rel'
-    _description = __doc__
     sale = fields.Many2One('sale.sale', 'Sale', ondelete='CASCADE',
         select=True, required=True)
     invoice = fields.Many2One('account.invoice', 'Invoice',
             ondelete='RESTRICT', select=True, required=True)
 
-SaleInvoice()
-
 
 class SaleIgnoredInvoice(ModelSQL):
     'Sale - Ignored Invoice'
-    _name = 'sale.sale-ignored-account.invoice'
+    __name__ = 'sale.sale-ignored-account.invoice'
     _table = 'sale_invoice_ignored_rel'
-    _description = __doc__
     sale = fields.Many2One('sale.sale', 'Sale', ondelete='CASCADE',
         select=True, required=True)
     invoice = fields.Many2One('account.invoice', 'Invoice',
             ondelete='RESTRICT', select=True, required=True)
 
-SaleIgnoredInvoice()
-
 
 class SaleRecreatedInvoice(ModelSQL):
     'Sale - Recreated Invoice'
-    _name = 'sale.sale-recreated-account.invoice'
+    __name__ = 'sale.sale-recreated-account.invoice'
     _table = 'sale_invoice_recreated_rel'
-    _description = __doc__
     sale = fields.Many2One('sale.sale', 'Sale', ondelete='CASCADE',
         select=True, required=True)
     invoice = fields.Many2One('account.invoice', 'Invoice',
             ondelete='RESTRICT', select=True, required=True)
 
-SaleRecreatedInvoice()
-
 
 class SaleLine(ModelSQL, ModelView):
     'Sale Line'
-    _name = 'sale.line'
+    __name__ = 'sale.line'
     _rec_name = 'description'
-    _description = __doc__
-
     sale = fields.Many2One('sale.sale', 'Sale', ondelete='CASCADE',
         select=True)
     sequence = fields.Integer('Sequence',
@@ -1034,7 +923,7 @@
             '_parent_sale.party'],
         depends=['product', 'type', 'product_uom_category'])
     unit_digits = fields.Function(fields.Integer('Unit Digits',
-        on_change_with=['unit']), 'get_unit_digits')
+        on_change_with=['unit']), 'on_change_with_unit_digits')
     product = fields.Many2One('product.product', 'Product',
         domain=[('salable', '=', True)],
         states={
@@ -1054,7 +943,7 @@
     product_uom_category = fields.Function(
         fields.Many2One('product.uom.category', 'Product Uom Category',
             on_change_with=['product']),
-        'get_product_uom_category')
+        'on_change_with_product_uom_category')
     unit_price = fields.Numeric('Unit Price', digits=(16, 4),
         states={
             'invisible': Eval('type') != 'line',
@@ -1097,12 +986,13 @@
                 'invisible': Eval('type') != 'line',
                 },
             depends=['type']),
-        'get_delivery_date')
+        'on_change_with_delivery_date')
 
-    def __init__(self):
-        super(SaleLine, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(SaleLine, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._error_messages.update({
             'customer_location_required': 'The customer location is required!',
             'missing_account_revenue': 'It misses '
                     'an "Account Revenue" on product "%s"!',
@@ -1110,359 +1000,277 @@
                     'an "account Revenue" default property!',
             })
 
-    def init(self, module_name):
-        super(SaleLine, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(SaleLine, cls).__register__(module_name)
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 comment change into note
         if table.column_exist('comment'):
-            cursor.execute('UPDATE "' + self._table + '" SET note = comment')
+            cursor.execute('UPDATE "' + cls._table + '" SET note = comment')
             table.drop_column('comment', exception=True)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'line'
 
-    def default_unit_digits(self):
+    @staticmethod
+    def default_unit_digits():
         return 2
 
-    def on_change_with_unit_digits(self, vals):
-        uom_obj = Pool().get('product.uom')
-        if vals.get('unit'):
-            uom = uom_obj.browse(vals['unit'])
-            return uom.digits
+    def on_change_with_unit_digits(self, name=None):
+        if self.unit:
+            return self.unit.digits
         return 2
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            if line.unit:
-                res[line.id] = line.unit.digits
-            else:
-                res[line.id] = 2
-        return res
+    def get_move_done(self, name):
+        Uom = Pool().get('product.uom')
+        done = True
+        if not self.product:
+            return True
+        if self.product.type == 'service':
+            return True
+        skip_ids = set(x.id for x in self.moves_ignored)
+        skip_ids.update(x.id for x in self.moves_recreated)
+        quantity = self.quantity
+        for move in self.moves:
+            if move.state != 'done' \
+                    and move.id not in skip_ids:
+                done = False
+                break
+            quantity -= Uom.compute_qty(move.uom, move.quantity, self.unit)
+        if done:
+            if quantity > 0.0:
+                done = False
+        return done
 
-    def get_move_done(self, ids, name):
-        uom_obj = Pool().get('product.uom')
-        res = {}
-        for line in self.browse(ids):
-            val = True
-            if not line.product:
-                res[line.id] = True
-                continue
-            if line.product.type == 'service':
-                res[line.id] = True
-                continue
-            skip_ids = set(x.id for x in line.moves_ignored)
-            skip_ids.update(x.id for x in line.moves_recreated)
-            quantity = line.quantity
-            for move in line.moves:
-                if move.state != 'done' \
-                        and move.id not in skip_ids:
-                    val = False
-                    break
-                quantity -= uom_obj.compute_qty(move.uom, move.quantity,
-                        line.unit)
-            if val:
-                if quantity > 0.0:
-                    val = False
-            res[line.id] = val
-        return res
+    def get_move_exception(self, name):
+        skip_ids = set(x.id for x in self.moves_ignored)
+        skip_ids.update(x.id for x in self.moves_recreated)
+        for move in self.moves:
+            if move.state == 'cancel' \
+                    and move.id not in skip_ids:
+                return True
+        return False
 
-    def get_move_exception(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            val = False
-            skip_ids = set(x.id for x in line.moves_ignored)
-            skip_ids.update(x.id for x in line.moves_recreated)
-            for move in line.moves:
-                if move.state == 'cancel' \
-                        and move.id not in skip_ids:
-                    val = True
-                    break
-            res[line.id] = val
-        return res
-
-    def _get_tax_rule_pattern(self, party, vals):
+    def _get_tax_rule_pattern(self):
         '''
         Get tax rule pattern
+        '''
+        return {}
 
-        :param party: the BrowseRecord of the party
-        :param vals: a dictionary with value from on_change
-        :return: a dictionary to use as pattern for tax rule
-        '''
-        res = {}
-        return res
-
-    def _get_context_sale_price(self, product, vals):
+    def _get_context_sale_price(self):
         context = {}
-        if vals.get('_parent_sale.currency'):
-            context['currency'] = vals['_parent_sale.currency']
-        if vals.get('_parent_sale.party'):
-            context['customer'] = vals['_parent_sale.party']
-        if vals.get('_parent_sale.sale_date'):
-            context['sale_date'] = vals['_parent_sale.sale_date']
-        if vals.get('unit'):
-            context['uom'] = vals['unit']
+        if self.sale:
+            if self.sale.currency:
+                context['currency'] = self.sale.currency.id
+            if self.sale.party:
+                context['customer'] = self.sale.party.id
+            if self.sale.sale_date:
+                context['sale_date'] = self.sale.sale_date
+        if self.unit:
+            context['uom'] = self.unit.id
         else:
-            context['uom'] = product.sale_uom.id
+            context['uom'] = self.product.sale_uom.id
         return context
 
-    def on_change_product(self, vals):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        product_obj = pool.get('product.product')
-        tax_rule_obj = pool.get('account.tax.rule')
+    def on_change_product(self):
+        Product = Pool().get('product.product')
 
-        if not vals.get('product'):
+        if not self.product:
             return {}
         res = {}
 
         party = None
         party_context = {}
-        if vals.get('_parent_sale.party'):
-            party = party_obj.browse(vals['_parent_sale.party'])
+        if self.sale and self.sale.party:
+            party = self.sale.party
             if party.lang:
                 party_context['language'] = party.lang.code
 
-        product = product_obj.browse(vals['product'])
-
-        with Transaction().set_context(
-                self._get_context_sale_price(product, vals)):
-            res['unit_price'] = product_obj.get_sale_price([product.id],
-                    vals.get('quantity') or 0)[product.id]
+        with Transaction().set_context(self._get_context_sale_price()):
+            res['unit_price'] = Product.get_sale_price([self.product],
+                    self.quantity or 0)[self.product.id]
             if res['unit_price']:
                 res['unit_price'] = res['unit_price'].quantize(
-                    Decimal(1) / 10 ** self.unit_price.digits[1])
+                    Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
         res['taxes'] = []
-        pattern = self._get_tax_rule_pattern(party, vals)
-        for tax in product.customer_taxes_used:
+        pattern = self._get_tax_rule_pattern()
+        for tax in self.product.customer_taxes_used:
             if party and party.customer_tax_rule:
-                tax_ids = tax_rule_obj.apply(party.customer_tax_rule, tax,
-                        pattern)
+                tax_ids = party.customer_tax_rule.apply(tax, pattern)
                 if tax_ids:
                     res['taxes'].extend(tax_ids)
                 continue
             res['taxes'].append(tax.id)
         if party and party.customer_tax_rule:
-            tax_ids = tax_rule_obj.apply(party.customer_tax_rule, None,
-                    pattern)
+            tax_ids = party.customer_tax_rule.apply(None, pattern)
             if tax_ids:
                 res['taxes'].extend(tax_ids)
 
-        if not vals.get('description'):
+        if not self.description:
             with Transaction().set_context(party_context):
-                res['description'] = product_obj.browse(product.id).rec_name
+                res['description'] = Product(self.product.id).rec_name
 
-        category = product.sale_uom.category
-        if not vals.get('unit') \
-                or vals.get('unit') not in [x.id for x in category.uoms]:
-            res['unit'] = product.sale_uom.id
-            res['unit.rec_name'] = product.sale_uom.rec_name
-            res['unit_digits'] = product.sale_uom.digits
+        category = self.product.sale_uom.category
+        if not self.unit or self.unit not in category.uoms:
+            res['unit'] = self.product.sale_uom.id
+            res['unit.rec_name'] = self.product.sale_uom.rec_name
+            res['unit_digits'] = self.product.sale_uom.digits
 
-        vals = vals.copy()
-        vals['unit_price'] = res['unit_price']
-        vals['type'] = 'line'
-        res['amount'] = self.on_change_with_amount(vals)
+        self.unit_price = res['unit_price']
+        self.type = 'line'
+        res['amount'] = self.on_change_with_amount()
         return res
 
-    def on_change_with_product_uom_category(self, values):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        if values.get('product'):
-            product = product_obj.browse(values['product'])
-            return product.default_uom_category.id
+    def on_change_with_product_uom_category(self, name=None):
+        if self.product:
+            return self.product.default_uom_category.id
 
-    def get_product_uom_category(self, ids, name):
-        categories = {}
-        for line in self.browse(ids):
-            if line.product:
-                categories[line.id] = line.product.default_uom_category.id
-            else:
-                categories[line.id] = None
-        return categories
+    def on_change_quantity(self):
+        Product = Pool().get('product.product')
 
-    def on_change_quantity(self, vals):
-        product_obj = Pool().get('product.product')
-
-        if not vals.get('product'):
+        if not self.product:
             return {}
         res = {}
 
-        product = product_obj.browse(vals['product'])
-
         with Transaction().set_context(
-                self._get_context_sale_price(product, vals)):
-            res['unit_price'] = product_obj.get_sale_price([vals['product']],
-                    vals.get('quantity') or 0)[vals['product']]
+                self._get_context_sale_price()):
+            res['unit_price'] = Product.get_sale_price([self.product],
+                self.quantity or 0)[self.product.id]
             if res['unit_price']:
                 res['unit_price'] = res['unit_price'].quantize(
-                    Decimal(1) / 10 ** self.unit_price.digits[1])
+                    Decimal(1) / 10 ** self.__class__.unit_price.digits[1])
         return res
 
-    def on_change_unit(self, vals):
-        return self.on_change_quantity(vals)
+    def on_change_unit(self):
+        return self.on_change_quantity()
 
-    def on_change_with_amount(self, vals):
-        currency_obj = Pool().get('currency.currency')
-        if vals.get('type') == 'line':
-            currency = vals.get('_parent_sale.currency')
-            if currency and isinstance(currency, (int, long)):
-                currency = currency_obj.browse(vals['_parent_sale.currency'])
-            amount = Decimal(str(vals.get('quantity') or '0.0')) * \
-                    (vals.get('unit_price') or Decimal('0.0'))
+    def on_change_with_amount(self):
+        if self.type == 'line':
+            currency = self.sale.currency if self.sale else None
+            amount = Decimal(str(self.quantity or '0.0')) * \
+                (self.unit_price or Decimal('0.0'))
             if currency:
-                return currency_obj.round(currency, amount)
+                return currency.round(amount)
             return amount
         return Decimal('0.0')
 
-    def get_amount(self, ids, name):
-        currency_obj = Pool().get('currency.currency')
-        res = {}
-        for line in self.browse(ids):
-            if line.type == 'line':
-                res[line.id] = currency_obj.round(line.sale.currency,
-                        Decimal(str(line.quantity)) * line.unit_price)
-            elif line.type == 'subtotal':
-                res[line.id] = Decimal('0.0')
-                for line2 in line.sale.lines:
-                    if line2.type == 'line':
-                        res[line.id] += currency_obj.round(line2.sale.currency,
-                                Decimal(str(line2.quantity)) *
-                                        line2.unit_price)
-                    elif line2.type == 'subtotal':
-                        if line.id == line2.id:
-                            break
-                        res[line.id] = Decimal('0.0')
-            else:
-                res[line.id] = Decimal('0.0')
-        return res
+    def get_amount(self, name):
+        if self.type == 'line':
+            return self.sale.currency.round(
+                Decimal(str(self.quantity)) * self.unit_price)
+        elif self.type == 'subtotal':
+            amount = Decimal('0.0')
+            for line2 in self.sale.lines:
+                if line2.type == 'line':
+                    amount += line2.sale.currency.round(
+                        Decimal(str(line2.quantity)) * line2.unit_price)
+                elif line2.type == 'subtotal':
+                    if self == line2:
+                        break
+                    amount = Decimal('0.0')
+            return amount
+        return Decimal('0.0')
 
-    def get_warehouse(self, ids, name):
-        result = {}
-        for line in self.browse(ids):
-            result[line.id] = line.sale.warehouse.id
-        return result
+    def get_warehouse(self, name):
+        return self.sale.warehouse.id
 
-    def get_from_location(self, ids, name):
-        result = {}
-        for line in self.browse(ids):
-            if line.quantity >= 0:
-                if line.warehouse:
-                    result[line.id] = line.warehouse.output_location.id
-                else:
-                    result[line.id] = None
-            else:
-                result[line.id] = line.sale.party.customer_location.id
-        return result
+    def get_from_location(self, name):
+        if self.quantity >= 0:
+            if self.warehouse:
+                return self.warehouse.output_location.id
+        else:
+            return self.sale.party.customer_location.id
 
-    def get_to_location(self, ids, name):
-        result = {}
-        for line in self.browse(ids):
-            if line.quantity >= 0:
-                result[line.id] = line.sale.party.customer_location.id
-            else:
-                if line.warehouse:
-                    result[line.id] = line.warehouse.input_location.id
-                else:
-                    result[line.id] = None
-        return result
+    def get_to_location(self, name):
+        if self.quantity >= 0:
+            return self.sale.party.customer_location.id
+        else:
+            if self.warehouse:
+                return self.warehouse.input_location.id
 
-    def _compute_delivery_date(self, product, date):
-        product_obj = Pool().get('product.product')
-        if product:
-            return product_obj.compute_delivery_date(product, date=date)
-        else:
-            return None
+    def on_change_with_delivery_date(self, name=None):
+        if self.product:
+            date = self.sale.sale_date if self.sale else None
+            return self.product.compute_delivery_date(date=date)
 
-    def on_change_with_delivery_date(self, values):
-        product_obj = Pool().get('product.product')
-        if values.get('product'):
-            product = product_obj.browse(values['product'])
-            return self._compute_delivery_date(product,
-                values.get('_parent_sale.sale_date'))
-        return None
+    def get_invoice_line(self, invoice_type):
+        '''
+        Return a list of invoice lines for sale line according to invoice_type
+        '''
+        pool = Pool()
+        Uom = pool.get('product.uom')
+        Property = pool.get('ir.property')
+        InvoiceLine = pool.get('account.invoice.line')
 
-    def get_delivery_date(self, ids, name):
-        dates = {}
-        for line in self.browse(ids):
-            dates[line.id] = self._compute_delivery_date(line.product,
-                line.sale.sale_date)
-        return dates
-
-    def get_invoice_line(self, line, invoice_type):
-        '''
-        Return a list of invoice line values for sale line according to
-        invoice_type
-        '''
-        uom_obj = Pool().get('product.uom')
-        property_obj = Pool().get('ir.property')
-
-        res = {}
-        res['type'] = line.type
-        res['description'] = line.description
-        res['note'] = line.note
-        if line.type != 'line':
-            if (line.sale.invoice_method == 'order'
-                    and ((all(l.quantity >= 0 for l in line.sale.lines
+        with Transaction().set_user(0, set_context=True):
+            invoice_line = InvoiceLine()
+        invoice_line.type = self.type
+        invoice_line.description = self.description
+        invoice_line.note = self.note
+        if self.type != 'line':
+            if (self.sale.invoice_method == 'order'
+                    and ((all(l.quantity >= 0 for l in self.sale.lines
                                 if l.type == 'line')
                             and invoice_type == 'out_invoice')
-                        or (all(l.quantity <= 0 for l in line.sale.lines
+                        or (all(l.quantity <= 0 for l in self.sale.lines
                                 if l.type == 'line')
                             and invoice_type == 'out_credit_note'))):
-                return [res]
+                return [invoice_line]
             else:
                 return []
 
-        if (invoice_type == 'out_invoice') != (line.quantity >= 0):
+        if (invoice_type == 'out_invoice') != (self.quantity >= 0):
             return []
 
-        if (line.sale.invoice_method == 'order'
-                or not line.product
-                or line.product.type == 'service'):
-            quantity = abs(line.quantity)
+        if (self.sale.invoice_method == 'order'
+                or not self.product
+                or self.product.type == 'service'):
+            quantity = abs(self.quantity)
         else:
             quantity = 0.0
-            for move in line.moves:
+            for move in self.moves:
                 if move.state == 'done':
-                    quantity += uom_obj.compute_qty(move.uom, move.quantity,
-                            line.unit)
+                    quantity += Uom.compute_qty(move.uom, move.quantity,
+                        self.unit)
 
-        skip_ids = set(l.id for i in line.sale.invoices_recreated
+        skip_ids = set(l.id for i in self.sale.invoices_recreated
             for l in i.lines)
-        for invoice_line in line.invoice_lines:
+        for invoice_line in self.invoice_lines:
             if invoice_line.type != 'line':
                 continue
             if invoice_line.id not in skip_ids:
-                quantity -= uom_obj.compute_qty(invoice_line.unit,
-                        invoice_line.quantity, line.unit)
-        res['quantity'] = quantity
+                quantity -= Uom.compute_qty(invoice_line.unit,
+                    invoice_line.quantity, self.unit)
+        invoice_line.quantity = quantity
 
-        if res['quantity'] <= 0.0:
+        if invoice_line.quantity <= 0.0:
             return []
-        res['unit'] = line.unit.id
-        res['product'] = line.product.id
-        res['unit_price'] = line.unit_price
-        res['taxes'] = [('set', [x.id for x in line.taxes])]
-        if line.product:
-            res['account'] = line.product.account_revenue_used.id
-            if not res['account']:
+        invoice_line.unit = self.unit
+        invoice_line.product = self.product
+        invoice_line.unit_price = self.unit_price
+        invoice_line.taxes = self.taxes
+        if self.product:
+            invoice_line.account = self.product.account_revenue_used
+            if not invoice_line.account:
                 self.raise_user_error('missing_account_revenue',
-                        error_args=(line.product.rec_name,))
+                        error_args=(self.product.rec_name,))
         else:
             for model in ('product.template', 'product.category'):
-                res['account'] = property_obj.get('account_revenue', model)
-                if res['account']:
+                invoice_line.account = Property.get('account_revenue', model)
+                if invoice_line.account:
                     break
-            if not res['account']:
+            if not invoice_line.account:
                 self.raise_user_error('missing_account_revenue_property')
-        return [res]
+        return [invoice_line]
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, lines, default=None):
         if default is None:
             default = {}
         default = default.copy()
@@ -1470,118 +1278,104 @@
         default['moves_ignored'] = None
         default['moves_recreated'] = None
         default['invoice_lines'] = None
-        return super(SaleLine, self).copy(ids, default=default)
+        return super(SaleLine, cls).copy(lines, default=default)
 
-    def get_move(self, line, shipment_type):
+    def get_move(self, shipment_type):
         '''
-        Return move values for the sale line according ot shipment_type
+        Return moves for the sale line according ot shipment_type
         '''
-        uom_obj = Pool().get('product.uom')
+        pool = Pool()
+        Uom = pool.get('product.uom')
+        Move = pool.get('stock.move')
 
-        res = {}
-        if line.type != 'line':
+        if self.type != 'line':
             return
-        if not line.product:
+        if not self.product:
             return
-        if line.product.type == 'service':
+        if self.product.type == 'service':
             return
-        if (shipment_type == 'out') != (line.quantity >= 0):
+        if (shipment_type == 'out') != (self.quantity >= 0):
             return
 
-        if line.sale.shipment_method == 'order':
-            quantity = abs(line.quantity)
+        if self.sale.shipment_method == 'order':
+            quantity = abs(self.quantity)
         else:
             quantity = 0.0
-            for invoice_line in line.invoice_lines:
+            for invoice_line in self.invoice_lines:
                 if invoice_line.invoice.state in ('open', 'paid'):
-                    quantity += uom_obj.compute_qty(invoice_line.unit,
-                        invoice_line.quantity, line.unit)
+                    quantity += Uom.compute_qty(invoice_line.unit,
+                        invoice_line.quantity, self.unit)
 
-        skip_ids = set(x.id for x in line.moves_recreated)
-        for move in line.moves:
+        skip_ids = set(x.id for x in self.moves_recreated)
+        for move in self.moves:
             if move.id not in skip_ids:
-                quantity -= uom_obj.compute_qty(move.uom, move.quantity,
-                        line.unit)
+                quantity -= Uom.compute_qty(move.uom, move.quantity,
+                    self.unit)
         if quantity <= 0.0:
             return
-        if not line.sale.party.customer_location:
+        if not self.sale.party.customer_location:
             self.raise_user_error('customer_location_required')
-        res['quantity'] = quantity
-        res['uom'] = line.unit.id
-        res['product'] = line.product.id
-        res['from_location'] = line.from_location.id
-        res['to_location'] = line.to_location.id
-        res['state'] = 'draft'
-        res['company'] = line.sale.company.id
-        res['unit_price'] = line.unit_price
-        res['currency'] = line.sale.currency.id
-        res['planned_date'] = line.delivery_date
-        return res
-
-SaleLine()
+        with Transaction().set_user(0, set_context=True):
+            move = Move()
+        move.quantity = quantity
+        move.uom = self.unit
+        move.product = self.product
+        move.from_location = self.from_location
+        move.to_location = self.to_location
+        move.state = 'draft'
+        move.company = self.sale.company
+        move.unit_price = self.unit_price
+        move.currency = self.sale.currency
+        move.planned_date = self.delivery_date
+        return move
 
 
 class SaleLineTax(ModelSQL):
     'Sale Line - Tax'
-    _name = 'sale.line-account.tax'
+    __name__ = 'sale.line-account.tax'
     _table = 'sale_line_account_tax'
-    _description = __doc__
     line = fields.Many2One('sale.line', 'Sale Line', ondelete='CASCADE',
             select=True, required=True)
     tax = fields.Many2One('account.tax', 'Tax', ondelete='RESTRICT',
             select=True, required=True)
 
-SaleLineTax()
-
 
 class SaleLineInvoiceLine(ModelSQL):
     'Sale Line - Invoice Line'
-    _name = 'sale.line-account.invoice.line'
+    __name__ = 'sale.line-account.invoice.line'
     _table = 'sale_line_invoice_lines_rel'
-    _description = __doc__
     sale_line = fields.Many2One('sale.line', 'Sale Line', ondelete='CASCADE',
             select=True, required=True)
     invoice_line = fields.Many2One('account.invoice.line', 'Invoice Line',
             ondelete='RESTRICT', select=True, required=True)
 
-SaleLineInvoiceLine()
-
 
 class SaleLineIgnoredMove(ModelSQL):
     'Sale Line - Ignored Move'
-    _name = 'sale.line-ignored-stock.move'
+    __name__ = 'sale.line-ignored-stock.move'
     _table = 'sale_line_moves_ignored_rel'
-    _description = __doc__
     sale_line = fields.Many2One('sale.line', 'Sale Line', ondelete='CASCADE',
             select=True, required=True)
     move = fields.Many2One('stock.move', 'Move', ondelete='RESTRICT',
             select=True, required=True)
 
-SaleLineIgnoredMove()
-
 
 class SaleLineRecreatedMove(ModelSQL):
     'Sale Line - Recreated Move'
-    _name = 'sale.line-recreated-stock.move'
+    __name__ = 'sale.line-recreated-stock.move'
     _table = 'sale_line_moves_recreated_rel'
-    _description = __doc__
     sale_line = fields.Many2One('sale.line', 'Sale Line', ondelete='CASCADE',
             select=True, required=True)
     move = fields.Many2One('stock.move', 'Move', ondelete='RESTRICT',
             select=True, required=True)
 
-SaleLineRecreatedMove()
-
 
 class SaleReport(CompanyReport):
-    _name = 'sale.sale'
+    __name__ = 'sale.sale'
 
-SaleReport()
 
-
-class Template(ModelSQL, ModelView):
-    _name = 'product.template'
-
+class Template:
+    __name__ = 'product.template'
     salable = fields.Boolean('Salable', states={
             'readonly': ~Eval('active', True),
             }, depends=['active'])
@@ -1603,214 +1397,194 @@
         depends=['active', 'salable'],
         help='In number of days')
 
-    def __init__(self):
-        super(Template, self).__init__()
-        self.account_revenue = copy.copy(self.account_revenue)
-        self.account_revenue.states = copy.copy(self.account_revenue.states)
+    @classmethod
+    def __setup__(cls):
+        super(Template, cls).__setup__()
         required = ~Eval('account_category', False) & Eval('salable', False)
-        if not self.account_revenue.states.get('required'):
-            self.account_revenue.states['required'] = required
+        if not cls.account_revenue.states.get('required'):
+            cls.account_revenue.states['required'] = required
         else:
-            self.account_revenue.states['required'] = (
-                    self.account_revenue.states['required'] | required)
-        if 'account_category' not in self.account_revenue.depends:
-            self.account_revenue = copy.copy(self.account_revenue)
-            self.account_revenue.depends = \
-                    copy.copy(self.account_revenue.depends)
-            self.account_revenue.depends.append('account_category')
-        if 'salable' not in self.account_revenue.depends:
-            self.account_revenue = copy.copy(self.account_revenue)
-            self.account_revenue.depends = \
-                    copy.copy(self.account_revenue.depends)
-            self.account_revenue.depends.append('salable')
-        self._reset_columns()
+            cls.account_revenue.states['required'] = (
+                    cls.account_revenue.states['required'] | required)
+        if 'account_category' not in cls.account_revenue.depends:
+            cls.account_revenue.depends.append('account_category')
+        if 'salable' not in cls.account_revenue.depends:
+            cls.account_revenue.depends.append('salable')
 
-    def default_salable(self):
+    @staticmethod
+    def default_salable():
         return True if Transaction().context.get('salable') else False
 
-    def default_delivery_time(self):
+    @staticmethod
+    def default_delivery_time():
         return 0
 
-    def on_change_with_sale_uom(self, vals):
-        uom_obj = Pool().get('product.uom')
-        res = None
+    def on_change_with_sale_uom(self):
+        if self.default_uom:
+            if self.sale_uom:
+                if self.default_uom.category == self.sale_uom.category:
+                    return self.sale_uom.id
+                else:
+                    return self.default_uom.id
+            else:
+                return self.default_uom.id
 
-        if vals.get('default_uom'):
-            default_uom = uom_obj.browse(vals['default_uom'])
-            if vals.get('sale_uom'):
-                sale_uom = uom_obj.browse(vals['sale_uom'])
-                if default_uom.category.id == sale_uom.category.id:
-                    res = sale_uom.id
-                else:
-                    res = default_uom.id
-            else:
-                res = default_uom.id
-        return res
 
-Template()
+class Product:
+    __name__ = 'product.product'
 
-
-class Product(ModelSQL, ModelView):
-    _name = 'product.product'
-
-    def get_sale_price(self, ids, quantity=0):
+    @staticmethod
+    def get_sale_price(products, quantity=0):
         '''
-        Return the sale price for product ids.
-
-        :param ids: the product ids
-        :param quantity: the quantity of the products
+        Return the sale price for products and quantity.
+        It uses if exists from the context:
             uom: the unit of measure
             currency: the currency id for the returned price
-        :return: a dictionary with for each product ids keys the computed price
         '''
         pool = Pool()
-        uom_obj = pool.get('product.uom')
-        user_obj = pool.get('res.user')
-        currency_obj = pool.get('currency.currency')
-        date_obj = pool.get('ir.date')
+        Uom = pool.get('product.uom')
+        User = pool.get('res.user')
+        Currency = pool.get('currency.currency')
+        Date = pool.get('ir.date')
 
-        today = date_obj.today()
-        res = {}
+        today = Date.today()
+        prices = {}
 
         uom = None
         if Transaction().context.get('uom'):
-            uom = uom_obj.browse(Transaction().context.get('uom'))
+            uom = Uom(Transaction().context.get('uom'))
 
         currency = None
         if Transaction().context.get('currency'):
-            currency = currency_obj.browse(
-                    Transaction().context.get('currency'))
+            currency = Currency(Transaction().context.get('currency'))
 
-        user2 = user_obj.browse(Transaction().user)
+        user = User(Transaction().user)
 
-        for product in self.browse(ids):
-            res[product.id] = product.list_price
+        for product in products:
+            prices[product.id] = product.list_price
             if uom:
-                res[product.id] = uom_obj.compute_price(
-                        product.default_uom, res[product.id], uom)
-            if currency and user2.company:
-                if user2.company.currency.id != currency.id:
+                prices[product.id] = Uom.compute_price(
+                    product.default_uom, prices[product.id], uom)
+            if currency and user.company:
+                if user.company.currency != currency:
                     date = Transaction().context.get('sale_date') or today
                     with Transaction().set_context(date=date):
-                        res[product.id] = currency_obj.compute(
-                                user2.company.currency.id, res[product.id],
-                                currency.id, round=False)
-        return res
+                        prices[product.id] = Currency.compute(
+                            user.company.currency, prices[product.id],
+                            currency, round=False)
+        return prices
 
-    def compute_delivery_date(self, product, date=None):
+    def compute_delivery_date(self, date=None):
         '''
-        Compute the delivery date for the Product at a the given date
+        Compute the delivery date a the given date
         '''
-        date_obj = Pool().get('ir.date')
+        Date = Pool().get('ir.date')
 
         if not date:
-            date = date_obj.today()
-        return date + datetime.timedelta(product.delivery_time)
+            date = Date.today()
+        return date + datetime.timedelta(self.delivery_time)
 
-Product()
 
+class ShipmentOut:
+    __name__ = 'stock.shipment.out'
 
-class ShipmentOut(ModelSQL, ModelView):
-    _name = 'stock.shipment.out'
-
-    def __init__(self):
-        super(ShipmentOut, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentOut, cls).__setup__()
+        cls._error_messages.update({
                 'reset_move': 'You cannot reset to draft a move generated '
                     'by a sale.',
             })
 
-    def write(self, ids, vals):
-        sale_obj = Pool().get('sale.sale')
-        sale_line_obj = Pool().get('sale.line')
+    @classmethod
+    def write(cls, shipments, vals):
+        pool = Pool()
+        Sale = pool.get('sale.sale')
+        SaleLine = pool.get('sale.line')
 
-        res = super(ShipmentOut, self).write(ids, vals)
+        super(ShipmentOut, cls).write(shipments, vals)
 
         if 'state' in vals and vals['state'] in ('done', 'cancel'):
-            sale_ids = []
+            sales = []
             move_ids = []
-            if isinstance(ids, (int, long)):
-                ids = [ids]
-            for shipment in self.browse(ids):
+            for shipment in shipments:
                 move_ids.extend([x.id for x in shipment.outgoing_moves])
 
-            sale_line_ids = sale_line_obj.search([
-                ('moves', 'in', move_ids),
-                ])
-            if sale_line_ids:
-                for sale_line in sale_line_obj.browse(sale_line_ids):
-                    if sale_line.sale.id not in sale_ids:
-                        sale_ids.append(sale_line.sale.id)
+            sale_lines = SaleLine.search([
+                    ('moves', 'in', move_ids),
+                    ])
+            if sale_lines:
+                for sale_line in sale_lines:
+                    if sale_line.sale not in sales:
+                        sales.append(sale_line.sale)
 
             with Transaction().set_user(0, set_context=True):
-                sale_obj.process(sale_ids)
-        return res
+                sales = Sale.browse([s.id for s in sales])
+            Sale.process(sales)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
-        for shipment in self.browse(ids):
+    def draft(cls, shipments):
+        for shipment in shipments:
             for move in shipment.outgoing_moves:
                 if move.state == 'cancel' and move.sale_line:
-                    self.raise_user_error('reset_move')
+                    cls.raise_user_error('reset_move')
 
-        return super(ShipmentOut, self).draft(ids)
+        return super(ShipmentOut, cls).draft(shipments)
 
-ShipmentOut()
 
+class ShipmentOutReturn:
+    __name__ = 'stock.shipment.out.return'
 
-class ShipmentOutReturn(ModelSQL, ModelView):
-    _name = 'stock.shipment.out.return'
-
-    def __init__(self):
-        super(ShipmentOutReturn, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentOutReturn, cls).__setup__()
+        cls._error_messages.update({
                 'reset_move': 'You cannot reset to draft a move generated '
                     'by a sale.',
             })
 
-    def write(self, ids, vals):
-        sale_obj = Pool().get('sale.sale')
-        sale_line_obj = Pool().get('sale.line')
+    @classmethod
+    def write(cls, shipments, vals):
+        pool = Pool()
+        Sale = pool.get('sale.sale')
+        SaleLine = pool.get('sale.line')
 
-        res = super(ShipmentOutReturn, self).write(ids, vals)
+        super(ShipmentOutReturn, cls).write(shipments, vals)
 
         if 'state' in vals and vals['state'] == 'received':
-            sale_ids = []
+            sales = []
             move_ids = []
-            if isinstance(ids, (int, long)):
-                ids = [ids]
-            for shipment in self.browse(ids):
+            for shipment in shipments:
                 move_ids.extend([x.id for x in shipment.incoming_moves])
 
-            sale_line_ids = sale_line_obj.search([
-                ('moves', 'in', move_ids),
-                ])
-            if sale_line_ids:
-                for sale_line in sale_line_obj.browse(sale_line_ids):
-                    if sale_line.sale.id not in sale_ids:
-                        sale_ids.append(sale_line.sale.id)
+            sale_lines = SaleLine.search([
+                    ('moves', 'in', move_ids),
+                    ])
+            if sale_lines:
+                for sale_line in sale_lines:
+                    if sale_line.sale not in sales:
+                        sales.append(sale_line.sale)
 
             with Transaction().set_user(0, set_context=True):
-                sale_obj.process(sale_ids)
-        return res
+                sales = Sale.browse([s.id for s in sales])
+            Sale.process(sales)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
-        for shipment in self.browse(ids):
+    def draft(cls, shipments):
+        for shipment in shipments:
             for move in shipment.incoming_moves:
                 if move.state == 'cancel' and move.sale_line:
-                    self.raise_user_error('reset_move')
+                    cls.raise_user_error('reset_move')
 
-        return super(ShipmentOutReturn, self).draft(ids)
+        return super(ShipmentOutReturn, cls).draft(shipments)
 
-ShipmentOutReturn()
 
-
-class Move(ModelSQL, ModelView):
-    _name = 'stock.move'
-
+class Move:
+    __name__ = 'stock.move'
     sale_line = fields.Many2One('sale.line', 'Sale Line', select=True,
         states={
             'readonly': Eval('state') != 'draft',
@@ -1824,197 +1598,118 @@
         ('recreated', 'Recreated'),
         ], 'Exception State'), 'get_sale_exception_state')
 
-    def get_sale(self, ids, name):
-        res = {}
-        for move in self.browse(ids):
-            res[move.id] = None
-            if move.sale_line:
-                res[move.id] = move.sale_line.sale.id
-        return res
+    def get_sale(self, name):
+        if self.sale_line:
+            return self.sale_line.sale.id
 
-    def search_sale(self, name, clause):
+    @classmethod
+    def search_sale(cls, name, clause):
         return [('sale_line.' + name,) + clause[1:]]
 
-    def get_sale_exception_state(self, ids, name):
-        res = {}.fromkeys(ids, '')
-        for move in self.browse(ids):
-            if not move.sale_line:
-                continue
-            if move.id in (x.id for x in move.sale_line.moves_recreated):
-                res[move.id] = 'recreated'
-            if move.id in (x.id for x in move.sale_line.moves_ignored):
-                res[move.id] = 'ignored'
-        return res
+    def get_sale_exception_state(self, name):
+        if not self.sale_line:
+            return ''
+        if self in self.sale_line.moves_recreated:
+            return 'recreated'
+        if self in self.sale_line.moves_ignored:
+            return 'ignored'
+        return ''
 
-    def write(self, ids, vals):
-        sale_obj = Pool().get('sale.sale')
-        sale_line_obj = Pool().get('sale.line')
+    @classmethod
+    def write(cls, moves, vals):
+        pool = Pool()
+        Sale = pool.get('sale.sale')
+        SaleLine = pool.get('sale.line')
 
-        res = super(Move, self).write(ids, vals)
+        super(Move, cls).write(moves, vals)
         if 'state' in vals and vals['state'] in ('cancel',):
-            if isinstance(ids, (int, long)):
-                ids = [ids]
-            sale_ids = set()
-            sale_line_ids = sale_line_obj.search([
-                ('moves', 'in', ids),
+            sales = set()
+            sale_lines = SaleLine.search([
+                    ('moves', 'in', [m.id for m in moves]),
+                    ])
+            if sale_lines:
+                for sale_line in sale_lines:
+                    sales.add(sale_line.sale)
+            if sales:
+                with Transaction().set_user(0, set_context=True):
+                    sales = Sale.browse([s.id for s in sales])
+                Sale.process(sales)
+
+    @classmethod
+    def delete(cls, moves):
+        pool = Pool()
+        Sale = pool.get('sale.sale')
+        SaleLine = pool.get('sale.line')
+
+        sales = set()
+        sale_lines = SaleLine.search([
+                ('moves', 'in', [m.id for m in moves]),
                 ])
-            if sale_line_ids:
-                for sale_line in sale_line_obj.browse(sale_line_ids):
-                    sale_ids.add(sale_line.sale.id)
-            if sale_ids:
+
+        super(Move, cls).delete(moves)
+
+        if sale_lines:
+            for sale_line in sale_lines:
+                sales.add(sale_line.sale)
+            if sales:
                 with Transaction().set_user(0, set_context=True):
-                    sale_obj.process(list(sale_ids))
-        return res
-
-    def delete(self, ids):
-        sale_obj = Pool().get('sale.sale')
-        sale_line_obj = Pool().get('sale.line')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        sale_ids = set()
-        sale_line_ids = sale_line_obj.search([
-            ('moves', 'in', ids),
-            ])
-
-        res = super(Move, self).delete(ids)
-
-        if sale_line_ids:
-            for sale_line in sale_line_obj.browse(sale_line_ids):
-                sale_ids.add(sale_line.sale.id)
-            if sale_ids:
-                with Transaction().set_user(0, set_context=True):
-                    sale_obj.process(list(sale_ids))
-        return res
-
-Move()
-
-
-class Invoice(ModelSQL, ModelView):
-    _name = 'account.invoice'
-
-    sale_exception_state = fields.Function(fields.Selection([
-        ('', ''),
-        ('ignored', 'Ignored'),
-        ('recreated', 'Recreated'),
-        ], 'Exception State'), 'get_sale_exception_state')
-
-    def __init__(self):
-        super(Invoice, self).__init__()
-        self._error_messages.update({
-            'delete_sale_invoice': 'You can not delete invoices '
-                    'that come from a sale!',
-            'reset_invoice_sale': 'You cannot reset to draft '
-                    'an invoice generated by a sale.',
-            })
-
-    @Workflow.transition('draft')
-    def draft(self, ids):
-        sale_obj = Pool().get('sale.sale')
-        sale_ids = sale_obj.search([
-            ('invoices', 'in', ids),
-            ])
-
-        if sale_ids:
-            self.raise_user_error('reset_invoice_sale')
-
-        return super(Invoice, self).draft(ids)
-
-    def get_sale_exception_state(self, ids, name):
-        sale_obj = Pool().get('sale.sale')
-        sale_ids = sale_obj.search([
-            ('invoices', 'in', ids),
-            ])
-
-        sales = sale_obj.browse(sale_ids)
-
-        recreated_ids = tuple(i.id for p in sales
-            for i in p.invoices_recreated)
-        ignored_ids = tuple(i.id for p in sales for i in p.invoices_ignored)
-
-        res = {}.fromkeys(ids, '')
-        for invoice in self.browse(ids):
-            if invoice.id in recreated_ids:
-                res[invoice.id] = 'recreated'
-            elif invoice.id in ignored_ids:
-                res[invoice.id] = 'ignored'
-
-        return res
-
-    def delete(self, ids):
-        if not ids:
-            return True
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        Transaction().cursor.execute('SELECT id FROM sale_invoices_rel '
-                'WHERE invoice IN (' + ','.join(('%s',) * len(ids)) + ')',
-                ids)
-        if Transaction().cursor.fetchone():
-            self.raise_user_error('delete_sale_invoice')
-        return super(Invoice, self).delete(ids)
-
-Invoice()
+                    sales = Sale.browse([s.id for s in sales])
+                Sale.process(list(sales))
 
 
 class OpenCustomer(Wizard):
     'Open Customers'
-    _name = 'sale.open_customer'
+    __name__ = 'sale.open_customer'
     start_state = 'open_'
     open_ = StateAction('party.act_party_form')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        wizard_obj = pool.get('ir.action.wizard')
-        Transaction().cursor.execute("SELECT DISTINCT(party) FROM sale_sale")
+        ModelData = pool.get('ir.model.data')
+        Wizard = pool.get('ir.action.wizard')
+        cursor = Transaction().cursor
+        cursor.execute("SELECT DISTINCT(party) FROM sale_sale")
         customer_ids = [line[0] for line in Transaction().cursor.fetchall()]
         action['pyson_domain'] = PYSONEncoder().encode(
             [('id', 'in', customer_ids)])
 
-        model_data_ids = model_data_obj.search([
-            ('fs_id', '=', 'act_open_customer'),
-            ('module', '=', 'sale'),
-            ('inherit', '=', None),
-            ], limit=1)
-        model_data = model_data_obj.browse(model_data_ids[0])
-        wizard = wizard_obj.browse(model_data.db_id)
+        model_data, = ModelData.search([
+                ('fs_id', '=', 'act_open_customer'),
+                ('module', '=', 'sale'),
+                ('inherit', '=', None),
+                ], limit=1)
+        wizard = Wizard.browse(model_data.db_id)
 
         action['name'] = wizard.name
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
 
-OpenCustomer()
-
 
 class HandleShipmentExceptionAsk(ModelView):
     'Handle Shipment Exception'
-    _name = 'sale.handle.shipment.exception.ask'
-    _description = __doc__
-
+    __name__ = 'sale.handle.shipment.exception.ask'
     recreate_moves = fields.Many2Many(
         'stock.move', None, None, 'Recreate Moves',
         domain=[('id', 'in', Eval('domain_moves'))], depends=['domain_moves'])
     domain_moves = fields.Many2Many(
         'stock.move', None, None, 'Domain Moves')
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         cursor.execute("UPDATE ir_model "
                 "SET model = REPLACE(model, 'packing', 'shipment') "
                 "WHERE model like '%%packing%%' AND module = %s",
                 (module_name,))
-        super(HandleShipmentExceptionAsk, self).init(module_name)
-
-HandleShipmentExceptionAsk()
+        super(HandleShipmentExceptionAsk, cls).__register__(module_name)
 
 
 class HandleShipmentException(Wizard):
     'Handle Shipment Exception'
-    _name = 'sale.handle.shipment.exception'
+    __name__ = 'sale.handle.shipment.exception'
     start_state = 'ask'
     ask = StateView('sale.handle.shipment.exception.ask',
         'sale.handle_shipment_exception_ask_view_form', [
@@ -2023,59 +1718,53 @@
             ])
     handle = StateTransition()
 
-    def default_ask(self, session, fields):
-        sale_obj = Pool().get('sale.sale')
-        sale = sale_obj.browse(Transaction().context.get('active_id'))
+    def default_ask(self, fields):
+        Sale = Pool().get('sale.sale')
+        sale = Sale(Transaction().context.get('active_id'))
 
         moves = []
         for line in sale.lines:
-            skip_ids = set(x.id for x in line.moves_ignored)
-            skip_ids.update(x.id for x in line.moves_recreated)
+            skips = set(line.moves_ignored)
+            skips.update(line.moves_recreated)
             for move in line.moves:
-                if move.state == 'cancel' and move.id not in skip_ids:
+                if move.state == 'cancel' and move not in skips:
                     moves.append(move.id)
         return {
             'recreate_moves': moves,
             'domain_moves': moves,
             }
 
-    def transition_handle(self, session):
+    def transition_handle(self):
         pool = Pool()
-        sale_obj = pool.get('sale.sale')
-        sale_line_obj = pool.get('sale.line')
-        to_recreate = [x.id for x in session.ask.recreate_moves]
-        domain_moves = [x.id for x in session.ask.domain_moves]
+        Sale = pool.get('sale.sale')
+        SaleLine = pool.get('sale.line')
 
-        sale = sale_obj.browse(Transaction().context['active_id'])
+        sale = Sale(Transaction().context['active_id'])
 
         for line in sale.lines:
             moves_ignored = []
             moves_recreated = []
-            skip_ids = set(x.id for x in line.moves_ignored)
-            skip_ids.update(x.id for x in line.moves_recreated)
+            skips = set(line.moves_ignored)
+            skips.update(line.moves_recreated)
             for move in line.moves:
-                if move.id not in domain_moves or move.id in skip_ids:
+                if move not in self.ask.domain_moves or move in skips:
                     continue
-                if move.id in to_recreate:
+                if move in self.ask.recreate_moves:
                     moves_recreated.append(move.id)
                 else:
                     moves_ignored.append(move.id)
 
-            sale_line_obj.write(line.id, {
+            SaleLine.write([line], {
                     'moves_ignored': [('add', moves_ignored)],
                     'moves_recreated': [('add', moves_recreated)],
                     })
-        sale_obj.process([sale.id])
+        Sale.process([sale])
         return 'end'
 
-HandleShipmentException()
-
 
 class HandleInvoiceExceptionAsk(ModelView):
     'Handle Invoice Exception'
-    _name = 'sale.handle.invoice.exception.ask'
-    _description = __doc__
-
+    __name__ = 'sale.handle.invoice.exception.ask'
     recreate_invoices = fields.Many2Many(
         'account.invoice', None, None, 'Recreate Invoices',
         domain=[('id', 'in', Eval('domain_invoices'))],
@@ -2085,12 +1774,10 @@
     domain_invoices = fields.Many2Many(
         'account.invoice', None, None, 'Domain Invoices')
 
-HandleInvoiceExceptionAsk()
-
 
 class HandleInvoiceException(Wizard):
     'Handle Invoice Exception'
-    _name = 'sale.handle.invoice.exception'
+    __name__ = 'sale.handle.invoice.exception'
     start_state = 'ask'
     ask = StateView('sale.handle.invoice.exception.ask',
         'sale.handle_invoice_exception_ask_view_form', [
@@ -2099,65 +1786,57 @@
             ])
     handle = StateTransition()
 
-    def default_ask(self, session, fields):
-        sale_obj = Pool().get('sale.sale')
+    def default_ask(self, fields):
+        Sale = Pool().get('sale.sale')
 
-        sale = sale_obj.browse(Transaction().context['active_id'])
-        skip_ids = set(x.id for x in sale.invoices_ignored)
-        skip_ids.update(x.id for x in sale.invoices_recreated)
+        sale = Sale(Transaction().context['active_id'])
+        skips = set(sale.invoices_ignored)
+        skips.update(sale.invoices_recreated)
         invoices = []
         for invoice in sale.invoices:
-            if invoice.state == 'cancel' and invoice.id not in skip_ids:
+            if invoice.state == 'cancel' and invoice not in skips:
                 invoices.append(invoice.id)
         return {
             'recreate_invoices': invoices,
             'domain_invoices': invoices,
             }
 
-    def transition_handle(self, session):
-        sale_obj = Pool().get('sale.sale')
-        to_recreate = [x.id for x in session.ask.recreate_invoices]
-        domain_invoices = [x.id for x in session.ask.domain_invoices]
+    def transition_handle(self):
+        Sale = Pool().get('sale.sale')
 
-        sale = sale_obj.browse(Transaction().context['active_id'])
+        sale = Sale(Transaction().context['active_id'])
 
-        skip_ids = set(x.id for x in sale.invoices_ignored)
-        skip_ids.update(x.id for x in sale.invoices_recreated)
+        skips = set(sale.invoices_ignored)
+        skips.update(sale.invoices_recreated)
         invoices_ignored = []
         invoices_recreated = []
         for invoice in sale.invoices:
-            if invoice.id not in domain_invoices or invoice.id in skip_ids:
+            if invoice not in self.ask.domain_invoices or invoice in skips:
                 continue
-            if invoice.id in to_recreate:
+            if invoice in self.ask.recreate_invoices:
                 invoices_recreated.append(invoice.id)
             else:
                 invoices_ignored.append(invoice.id)
 
-        sale_obj.write(sale.id, {
+        Sale.write([sale], {
                 'invoices_ignored': [('add', invoices_ignored)],
                 'invoices_recreated': [('add', invoices_recreated)],
                 })
-        sale_obj.process([sale.id])
+        Sale.process([sale])
         return 'end'
 
-HandleInvoiceException()
-
 
 class ReturnSale(Wizard):
-    _name = 'sale.return_sale'
+    __name__ = 'sale.return_sale'
     start_state = 'make_return'
     make_return = StateTransition()
 
-    def transition_make_return(self, session):
-        pool = Pool()
-        sale_obj = pool.get('sale.sale')
-        line_obj = pool.get('sale.line')
+    def transition_make_return(self):
+        Sale = Pool().get('sale.sale')
 
-        sale_id = Transaction().context['active_id']
-        new_sale_id = sale_obj.copy(sale_id)
-        new_line_ids = line_obj.search([('sale', '=', new_sale_id)])
-        for new_line in line_obj.browse(new_line_ids):
-            line_obj.write(new_line.id, {'quantity': -new_line.quantity})
+        sale = Sale(Transaction().context['active_id'])
+        new_sale, = Sale.copy([sale])
+        for new_line in new_sale.lines:
+            new_line.quantity *= -1
+            new_line.save()
         return 'end'
-
-ReturnSale()
diff -r e3c518c64971 trytond/modules/sale_opportunity/__init__.py
--- a/trytond/modules/sale_opportunity/__init__.py	Sun Sep 23 16:25:47 2012 +0200
+++ b/trytond/modules/sale_opportunity/__init__.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,4 +1,20 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .opportunity import *
+
+
+def register():
+    Pool.register(
+        SaleOpportunity,
+        SaleOpportunityLine,
+        SaleOpportunityHistory,
+        SaleOpportunityEmployee,
+        OpenSaleOpportunityEmployeeStart,
+        SaleOpportunityMonthly,
+        SaleOpportunityEmployeeMonthly,
+        module='sale_opportunity', type_='model')
+    Pool.register(
+        OpenSaleOpportunityEmployee,
+        module='sale_opportunity', type_='wizard')
diff -r e3c518c64971 trytond/modules/sale_opportunity/opportunity.py
--- a/trytond/modules/sale_opportunity/opportunity.py	Sun Sep 23 16:25:47 2012 +0200
+++ b/trytond/modules/sale_opportunity/opportunity.py	Mon Sep 24 20:36:42 2012 +0200
@@ -10,6 +10,11 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['SaleOpportunity', 'SaleOpportunityLine',
+    'SaleOpportunityHistory', 'SaleOpportunityEmployee',
+    'OpenSaleOpportunityEmployeeStart', 'OpenSaleOpportunityEmployee',
+    'SaleOpportunityMonthly', 'SaleOpportunityEmployeeMonthly']
+
 STATES = [
     ('lead', 'Lead'),
     ('opportunity', 'Opportunity'),
@@ -29,11 +34,9 @@
 
 class SaleOpportunity(Workflow, ModelSQL, ModelView):
     'Sale Opportunity'
-    _name = "sale.opportunity"
-    _description = __doc__
+    __name__ = "sale.opportunity"
     _history = True
     _rec_name = 'description'
-
     party = fields.Many2One('party.party', 'Party', required=True, select=True,
         on_change=['party'], states=_STATES_STOP, depends=_DEPENDS_STOP)
     address = fields.Many2One('party.address', 'Address',
@@ -88,19 +91,20 @@
             'invisible': Eval('state') != 'converted',
             }, depends=['state'])
 
-    def __init__(self):
-        super(SaleOpportunity, self).__init__()
-        self._order.insert(0, ('start_date', 'DESC'))
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(SaleOpportunity, cls).__setup__()
+        cls._order.insert(0, ('start_date', 'DESC'))
+        cls._sql_constraints += [
             ('check_percentage',
                 'CHECK(probability >= 0 AND probability <= 100)',
                 'Probability must be between 0 and 100!')
-        ]
-        self._error_messages.update({
+            ]
+        cls._error_messages.update({
                 'delete_cancel': 'Sale Opportunity "%s" must be cancelled '\
                     'before deletion!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('lead', 'opportunity'),
                 ('lead', 'lost'),
                 ('lead', 'cancelled'),
@@ -111,7 +115,7 @@
                 ('lost', 'lead'),
                 ('cancelled', 'lead'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'lead': {
                     'invisible': ~Eval('state').in_(
                         ['cancelled', 'lost', 'opportunity']),
@@ -132,202 +136,185 @@
                     },
                 })
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'lead'
 
-    def default_start_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_start_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
-    def default_probability(self):
+    @staticmethod
+    def default_probability():
         return 50
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_employee(self):
-        user_obj = Pool().get('res.user')
+    @staticmethod
+    def default_employee():
+        User = Pool().get('res.user')
 
         if Transaction().context.get('employee'):
             return Transaction().context['employee']
         else:
-            user = user_obj.browse(Transaction().user)
+            user = User(Transaction().user)
             if user.employee:
                 return user.employee.id
 
-    def default_payment_term(self):
-        payment_term_obj = Pool().get('account.invoice.payment_term')
-        payment_term_ids = payment_term_obj.search(self.payment_term.domain)
-        if len(payment_term_ids) == 1:
-            return payment_term_ids[0]
+    @classmethod
+    def default_payment_term(cls):
+        PaymentTerm = Pool().get('account.invoice.payment_term')
+        payment_terms = PaymentTerm.search(cls.payment_term.domain)
+        if len(payment_terms) == 1:
+            return payment_terms[0].id
 
-    def get_currency(self, ids, name):
+    def get_currency(self, name):
+        return self.company.currency.id
+
+    def get_currency_digits(self, name):
+        return self.company.currency.digits
+
+    def on_change_company(self):
         res = {}
-        for opportunity in self.browse(ids):
-            res[opportunity.id] = opportunity.company.currency.id
+        if self.company:
+            res['currency'] = self.company.currency.id
+            res['currency.rec_name'] = self.company.currency.rec_name
+            res['currency_digits'] = self.company.currency.digits
         return res
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for opportunity in self.browse(ids):
-            res[opportunity.id] = opportunity.company.currency.digits
-        return res
-
-    def on_change_company(self, values):
-        company_obj = Pool().get('company.company')
-
-        res = {}
-        if values.get('company'):
-            company = company_obj.browse(values['company'])
-            res['currency'] = company.currency.id
-            res['currency.rec_name'] = company.currency.rec_name
-            res['currency_digits'] = company.currency.digits
-        return res
-
-    def on_change_party(self, values):
-        party_obj = Pool().get('party.party')
-        payment_term_obj = Pool().get('account.invoice.payment_term')
+    def on_change_party(self):
+        PaymentTerm = Pool().get('account.invoice.payment_term')
 
         res = {
             'payment_term': None,
-        }
-        if values.get('party'):
-            party = party_obj.browse(values['party'])
-            if party.customer_payment_term:
-                res['payment_term'] = party.customer_payment_term.id
+            }
+        if self.party:
+            if self.party.customer_payment_term:
+                res['payment_term'] = self.party.customer_payment_term.id
+                res['payment_term.rec_name'] = \
+                    self.party.customer_payment_term.rec_name
         if not res['payment_term']:
             res['payment_term'] = self.default_payment_term()
-        if res['payment_term']:
-            res['payment_term.rec_name'] = payment_term_obj.browse(
-                    res['payment_term']).rec_name
+            res['payment_term.rec_name'] = PaymentTerm(
+                res['payment_term']).rec_name
+        print res
         return res
 
-    def _get_sale_line_opportunity_line(self, opportunity):
+    def _get_sale_line_opportunity_line(self, sale):
         '''
-        Return sale line values for each opportunity line
-
-        :param opportunity: the BrowseRecord of opportunity
-
-        :return: a dictionary with opportunity line id as key
-            and a dictionary of sale line values as value
+        Return sale lines for each opportunity line
         '''
-        line_obj = Pool().get('sale.opportunity.line')
         res = {}
-        for line in opportunity.lines:
+        for line in self.lines:
             if line.sale_line:
                 continue
-            val = line_obj.get_sale_line(line)
-            if val:
-                res[line.id] = val
+            sale_line = line.get_sale_line(sale)
+            if sale_line:
+                res[line.id] = sale_line
         return res
 
-    def _get_sale_opportunity(self, opportunity):
+    def _get_sale_opportunity(self):
         '''
-        Return sale values for an opportunity
+        Return sale for an opportunity
+        '''
+        Sale = Pool().get('sale.sale')
+        with Transaction().set_user(0, set_context=True):
+            return Sale(
+                description=self.description,
+                party=self.party,
+                payment_term=self.payment_term,
+                company=self.company,
+                invoice_address=self.address,
+                shipment_address=self.address,
+                currency=self.company.currency,
+                comment=self.comment,
+                sale_date=None,
+                )
 
-        :param opportunity: the BrowseRecord of the opportunity
+    def create_sale(self):
+        '''
+        Create a sale for the opportunity and return the sale
+        '''
+        Line = Pool().get('sale.opportunity.line')
 
-        :return: a dictionary with sale fields as key and sale values as value
-        '''
-        res = {
-            'description': opportunity.description,
-            'party': opportunity.party.id,
-            'payment_term': opportunity.payment_term.id,
-            'company': opportunity.company.id,
-            'invoice_address': opportunity.address and opportunity.address.id,
-            'shipment_address': opportunity.address and opportunity.address.id,
-            'currency': opportunity.company.currency.id,
-            'comment': opportunity.comment,
-        }
-        return res
-
-    def create_sale(self, opportunity):
-        '''
-        Create a sale for the opportunity and return the sale id
-        '''
-        pool = Pool()
-        sale_obj = pool.get('sale.sale')
-        sale_line_obj = pool.get('sale.line')
-        line_obj = pool.get('sale.opportunity.line')
-
-        if opportunity.sale:
+        if self.sale:
             return
 
-        values = self._get_sale_opportunity(opportunity)
-        sale_line_values = self._get_sale_line_opportunity_line(opportunity)
+        sale = self._get_sale_opportunity()
+        sale_lines = self._get_sale_line_opportunity_line(sale)
+        sale.save()
 
-        with Transaction().set_user(0, set_context=True):
-            sale_id = sale_obj.create(values)
+        for line_id, sale_line in sale_lines.iteritems():
+            sale_line.sale = sale
+            sale_line.save()
+            Line.write([Line(line_id)], {
+                    'sale_line': sale_line.id,
+                    })
 
-        for line_id, values in sale_line_values.iteritems():
-            values['sale'] = sale_id
-            with Transaction().set_user(0, set_context=True):
-                sale_line_id = sale_line_obj.create(values)
-            line_obj.write(line_id, {
-                'sale_line': sale_line_id,
+        self.write([self], {
+                'sale': sale.id,
+                })
+        return sale
+
+    @classmethod
+    def delete(cls, opportunities):
+        # Cancel before delete
+        cls.cancel(opportunities)
+        for opportunity in opportunities:
+            if opportunity.state != 'cancel':
+                cls.raise_user_error('delete_cancel', opportunity.rec_name)
+        super(SaleOpportunity, cls).delete(opportunities)
+
+    @classmethod
+    @ModelView.button
+    @Workflow.transition('lead')
+    def lead(cls, opportunities):
+        pass
+
+    @classmethod
+    @ModelView.button
+    @Workflow.transition('opportunity')
+    def opportunity(cls, opportunities):
+        pass
+
+    @classmethod
+    @ModelView.button
+    @Workflow.transition('converted')
+    def convert(cls, opportunities):
+        Date = Pool().get('ir.date')
+        cls.write(opportunities, {
+                'end_date': Date.today(),
+                })
+        for opportunity in opportunities:
+            opportunity.create_sale()
+
+    @classmethod
+    @ModelView.button
+    @Workflow.transition('lost')
+    def lost(cls, opportunities):
+        Date = Pool().get('ir.date')
+        cls.write(opportunities, {
+                'end_date': Date.today(),
                 })
 
-        self.write(opportunity.id, {
-            'sale': sale_id,
-            })
-        return sale_id
-
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        # Cancel before delete
-        self.cancel(ids)
-        for opportunity in self.browse(ids):
-            if opportunity.state != 'cancel':
-                self.raise_user_error('delete_cancel', opportunity.rec_name)
-        return super(SaleOpportunity, self).delete(ids)
-
-    @ModelView.button
-    @Workflow.transition('lead')
-    def lead(self, ids):
-        pass
-
-    @ModelView.button
-    @Workflow.transition('opportunity')
-    def opportunity(self, ids):
-        pass
-
-    @ModelView.button
-    @Workflow.transition('converted')
-    def convert(self, ids):
-        date_obj = Pool().get('ir.date')
-        self.write(ids, {
-                'end_date': date_obj.today(),
-                })
-        for opportunity in self.browse(ids):
-            self.create_sale(opportunity)
-
-    @ModelView.button
-    @Workflow.transition('lost')
-    def lost(self, ids):
-        date_obj = Pool().get('ir.date')
-        self.write(ids, {
-                'end_date': date_obj.today(),
-                })
-
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancelled')
-    def cancel(self, ids):
-        date_obj = Pool().get('ir.date')
-        self.write(ids, {
-                'end_date': date_obj.today(),
+    def cancel(cls, opportunities):
+        Date = Pool().get('ir.date')
+        cls.write(opportunities, {
+                'end_date': Date.today(),
                 })
 
-SaleOpportunity()
-
 
 class SaleOpportunityLine(ModelSQL, ModelView):
     'Sale Opportunity Line'
-    _name = "sale.opportunity.line"
-    _description = __doc__
+    __name__ = "sale.opportunity.line"
     _rec_name = "product"
     _history = True
-
     opportunity = fields.Many2One('sale.opportunity', 'Opportunity')
     sequence = fields.Integer('Sequence',
         order_field='(%(table)s.sequence IS NULL) %(order)s, '
@@ -338,97 +325,68 @@
             digits=(16, Eval('unit_digits', 2)), depends=['unit_digits'])
     unit = fields.Many2One('product.uom', 'Unit', required=True)
     unit_digits = fields.Function(fields.Integer('Unit Digits',
-        on_change_with=['unit']), 'get_unit_digits')
+        on_change_with=['unit']), 'on_change_with_unit_digits')
     sale_line = fields.Many2One('sale.line', 'Sale Line', readonly=True,
         states={
             'invisible': (Eval('_parent_opportunity', {}).get('state')
                 != 'converted'),
             })
 
-    def __init__(self):
-        super(SaleOpportunityLine, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(SaleOpportunityLine, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(SaleOpportunityLine, self).init(module_name)
+        super(SaleOpportunityLine, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-    def on_change_with_unit_digits(self, vals):
-        uom_obj = Pool().get('product.uom')
-        if vals.get('unit'):
-            uom = uom_obj.browse(vals['unit'])
-            return uom.digits
+    def on_change_with_unit_digits(self, name=None):
+        if self.unit:
+            return self.unit.digits
         return 2
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            if line.unit:
-                res[line.id] = line.unit.digits
-            else:
-                res[line.id] = 2
-        return res
-
-    def on_change_product(self, vals):
-        product_obj = Pool().get('product.product')
-
-        if not vals.get('product'):
+    def on_change_product(self):
+        if not self.product:
             return {}
         res = {}
 
-        product = product_obj.browse(vals['product'])
-        category = product.sale_uom.category
-        if not vals.get('unit') \
-                or vals.get('unit') not in [x.id for x in category.uoms]:
-            res['unit'] = product.sale_uom.id
-            res['unit.rec_name'] = product.sale_uom.rec_name
-            res['unit_digits'] = product.sale_uom.digits
+        category = self.product.sale_uom.category
+        if (not self.unit
+                or self.unit not in category.uoms):
+            res['unit'] = self.product.sale_uom.id
+            res['unit.rec_name'] = self.product.sale_uom.rec_name
+            res['unit_digits'] = self.product.sale_uom.digits
         return res
 
-    def get_sale_line(self, line):
+    def get_sale_line(self, sale):
         '''
-        Return sale line values for opportunity line
-
-        :param line: the BrowseRecord of the line
-
-        :return: a dictionary with sale line fields as key
-            and sale line values as value
+        Return sale line for opportunity line
         '''
-        sale_line_obj = Pool().get('sale.line')
-        res = {
-            'type': 'line',
-            'quantity': line.quantity,
-            'unit': line.unit.id,
-            'product': line.product.id,
-        }
-        res.update(sale_line_obj.on_change_product({
-            'product': line.product.id,
-            'unit': line.unit.id,
-            'quantity': line.quantity,
-            '_parent_sale.party': line.opportunity.party.id,
-            '_parent_sale.currency': line.opportunity.company.currency.id,
-            }))
-        for field_name, field in sale_line_obj._columns.iteritems():
-            if field._type not in ('one2many', 'many2many'):
-                continue
-            if field_name not in res:
-                continue
-            res[field_name] = [('add', res[field_name])]
-        return res
-
-SaleOpportunityLine()
+        SaleLine = Pool().get('sale.line')
+        with Transaction().set_user(0, set_context=True):
+            sale_line = SaleLine(
+                type='line',
+                quantity=self.quantity,
+                unit=self.unit,
+                product=self.product,
+                sale=sale,
+                description=None,
+                )
+        for k, v in sale_line.on_change_product().iteritems():
+            setattr(sale_line, k, v)
+        return sale_line
 
 
 class SaleOpportunityHistory(ModelSQL, ModelView):
     'Sale Opportunity History'
-    _name = 'sale.opportunity.history'
-    _description = __doc__
-
+    __name__ = 'sale.opportunity.history'
     date = fields.DateTime('Change Date')
     opportunity = fields.Many2One('sale.opportunity', 'Sale Opportunity')
     user = fields.Many2One('res.user', 'User')
@@ -454,13 +412,15 @@
         'invisible': Not(Equal(Eval('state'), 'lost')),
     }, depends=['state'])
 
-    def __init__(self):
-        super(SaleOpportunityHistory, self).__init__()
-        self._order.insert(0, ('date', 'DESC'))
+    @classmethod
+    def __setup__(cls):
+        super(SaleOpportunityHistory, cls).__setup__()
+        cls._order.insert(0, ('date', 'DESC'))
 
-    def _table_query_fields(self):
-        opportunity_obj = Pool().get('sale.opportunity')
-        table = '%s__history' % opportunity_obj._table
+    @classmethod
+    def _table_query_fields(cls):
+        Opportunity = Pool().get('sale.opportunity')
+        table = '%s__history' % Opportunity._table
         return [
             'MIN("%s".__id) AS id' % table,
             '"%s".id AS opportunity' % table,
@@ -469,44 +429,42 @@
             ('COALESCE("%s".write_uid, "%s".create_uid) AS user'
                 % (table, table)),
             ] + ['"%s"."%s"' % (table, name)
-                for name, field in self._columns.iteritems()
+                for name, field in cls._fields.iteritems()
                 if name not in ('id', 'opportunity', 'date', 'user')
                 and not hasattr(field, 'set')]
 
-    def _table_query_group(self):
-        opportunity_obj = Pool().get('sale.opportunity')
-        table = '%s__history' % opportunity_obj._table
+    @classmethod
+    def _table_query_group(cls):
+        Opportunity = Pool().get('sale.opportunity')
+        table = '%s__history' % Opportunity._table
         return [
             '"%s".id' % table,
             'COALESCE("%s".write_uid, "%s".create_uid)' % (table, table),
         ] + ['"%s"."%s"' % (table, name)
-                for name, field in self._columns.iteritems()
+                for name, field in cls._fields.iteritems()
                 if name not in ('id', 'opportunity', 'date', 'user')
                 and not hasattr(field, 'set')]
 
-    def table_query(self):
-        opportunity_obj = Pool().get('sale.opportunity')
-        return (('SELECT ' + \
-                (', '.join(self._table_query_fields())) + \
-                ' FROM "%s__history" '
-                'GROUP BY ' + \
-                (', '.join(self._table_query_group()))) % \
-                opportunity_obj._table, [])
+    @classmethod
+    def table_query(cls):
+        Opportunity = Pool().get('sale.opportunity')
+        return (('SELECT ' + (', '.join(cls._table_query_fields()))
+                + ' FROM "%s__history" GROUP BY '
+                + (', '.join(cls._table_query_group())))
+            % Opportunity._table, [])
 
-    def get_lines(self, ids, name):
-        line_obj = Pool().get('sale.opportunity.line')
-        histories = self.browse(ids)
-        result = {}
+    def get_lines(self, name):
+        Line = Pool().get('sale.opportunity.line')
         # We will always have only one id per call due to datetime_field
-        for history in histories:
-            result[history.id] = line_obj.search([
-                ('opportunity', '=', history.opportunity.id),
-            ])
-        return result
+        lines = Line.search([
+                ('opportunity', '=', self.opportunity.id),
+                ])
+        return [l.id for l in lines]
 
-    def read(self, ids, fields_names=None):
-        res = super(SaleOpportunityHistory, self).read(ids,
-                fields_names=fields_names)
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        res = super(SaleOpportunityHistory, cls).read(ids,
+            fields_names=fields_names)
 
         # Remove microsecond from timestamp
         for values in res:
@@ -518,14 +476,10 @@
                 values['date'] = values['date'].replace(microsecond=0)
         return res
 
-SaleOpportunityHistory()
-
 
 class SaleOpportunityEmployee(ModelSQL, ModelView):
     'Sale Opportunity per Employee'
-    _name = 'sale.opportunity_employee'
-    _description = __doc__
-
+    __name__ = 'sale.opportunity_employee'
     employee = fields.Many2One('company.employee', 'Employee')
     number = fields.Integer('Number')
     converted = fields.Integer('Converted')
@@ -545,14 +499,17 @@
     conversion_amount_rate = fields.Function(fields.Float(
         'Conversion Amount Rate', help='In %'), 'get_conversion_amount_rate')
 
-    def _converted_state(self):
+    @staticmethod
+    def _converted_state():
         return ['converted']
 
-    def _lost_state(self):
+    @staticmethod
+    def _lost_state():
         return ['lost']
 
-    def table_query(self):
-        opportunity_obj = Pool().get('sale.opportunity')
+    @classmethod
+    def table_query(cls):
+        Opportunity = Pool().get('sale.opportunity')
         clause = ' '
         args = [True]
         if Transaction().context.get('start_date'):
@@ -570,69 +527,48 @@
                     'company, '
                     'COUNT(1) AS number, '
                     'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._converted_state()) + ') '
+                        for x in cls._converted_state()) + ') '
                         'THEN 1 ELSE 0 END) AS converted, '
                     'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._lost_state()) + ') '
+                        for x in cls._lost_state()) + ') '
                         'THEN 1 ELSE 0 END) AS lost, '
                     'SUM(amount) AS amount, '
                     'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._converted_state()) + ') '
+                        for x in cls._converted_state()) + ') '
                         'THEN amount ELSE 0 END) AS converted_amount '
-                'FROM "' + opportunity_obj._table + '" '
-                'WHERE %s ' \
-                + clause + \
-                'GROUP BY employee, company', args)
+            'FROM "' + Opportunity._table + '" '
+            'WHERE %s ' + clause +
+            'GROUP BY employee, company', args)
 
-    def get_conversion_rate(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            if record.number:
-                res[record.id] = (float(record.converted)
-                    / record.number * 100.0)
-            else:
-                res[record.id] = 0.0
-        return res
+    def get_conversion_rate(self, name):
+        if self.number:
+            return float(self.converted) / self.number * 100.0
+        else:
+            return 0.0
 
-    def get_currency(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            res[record.id] = record.company.currency.id
-        return res
+    def get_currency(self, name):
+        return self.company.currency.id
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            res[record.id] = record.company.currency.digits
-        return res
+    def get_currency_digits(self, name):
+        return self.company.currency.digits
 
-    def get_conversion_amount_rate(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            if record.amount:
-                res[record.id] = (float(record.converted_amount) /
-                        float(record.amount) * 100.0)
-            else:
-                res[record.id] = 0.0
-        return res
-
-SaleOpportunityEmployee()
+    def get_conversion_amount_rate(self, name):
+        if self.amount:
+            return float(self.converted_amount) / float(self.amount) * 100.0
+        else:
+            return 0.0
 
 
 class OpenSaleOpportunityEmployeeStart(ModelView):
     'Open Sale Opportunity per Employee'
-    _name = 'sale.opportunity_employee.open.start'
-    _description = __doc__
+    __name__ = 'sale.opportunity_employee.open.start'
     start_date = fields.Date('Start Date')
     end_date = fields.Date('End Date')
 
-OpenSaleOpportunityEmployeeStart()
-
 
 class OpenSaleOpportunityEmployee(Wizard):
     'Open Sale Opportunity per Employee'
-    _name = 'sale.opportunity_employee.open'
-
+    __name__ = 'sale.opportunity_employee.open'
     start = StateView('sale.opportunity_employee.open.start',
         'sale_opportunity.opportunity_employee_open_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -640,21 +576,17 @@
             ])
     open_ = StateAction('sale_opportunity.act_opportunity_employee_form')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         action['pyson_context'] = PYSONEncoder().encode({
-                'start_date': session.start.start_date,
-                'end_date': session.start.end_date,
+                'start_date': self.start.start_date,
+                'end_date': self.start.end_date,
                 })
         return action, {}
 
-OpenSaleOpportunityEmployee()
-
 
 class SaleOpportunityMonthly(ModelSQL, ModelView):
     'Sale Opportunity per Month'
-    _name = 'sale.opportunity_monthly'
-    _description = __doc__
-
+    __name__ = 'sale.opportunity_monthly'
     year = fields.Char('Year')
     month = fields.Integer('Month')
     year_month = fields.Function(fields.Char('Year-Month'),
@@ -677,21 +609,25 @@
     conversion_amount_rate = fields.Function(fields.Float(
         'Conversion Amount Rate', help='In %'), 'get_conversion_amount_rate')
 
-    def __init__(self):
-        super(SaleOpportunityMonthly, self).__init__()
-        self._order.insert(0, ('year', 'DESC'))
-        self._order.insert(1, ('month', 'DESC'))
+    @classmethod
+    def __setup__(cls):
+        super(SaleOpportunityMonthly, cls).__setup__()
+        cls._order.insert(0, ('year', 'DESC'))
+        cls._order.insert(1, ('month', 'DESC'))
 
-    def _converted_state(self):
+    @staticmethod
+    def _converted_state():
         return ['converted']
 
-    def _lost_state(self):
+    @staticmethod
+    def _lost_state():
         return ['lost']
 
-    def table_query(self):
-        opportunity_obj = Pool().get('sale.opportunity')
-        type_id = FIELDS[self.id._type].sql_type(self.id)[0]
-        type_year = FIELDS[self.year._type].sql_type(self.year)[0]
+    @classmethod
+    def table_query(cls):
+        Opportunity = Pool().get('sale.opportunity')
+        type_id = FIELDS[cls.id._type].sql_type(cls.id)[0]
+        type_year = FIELDS[cls.year._type].sql_type(cls.year)[0]
         return ('SELECT CAST(id AS ' + type_id + ') AS id, create_uid, '
                     'create_date, write_uid, write_date, '
                     'CAST(year AS ' + type_year + ') AS year, month, company, '
@@ -708,65 +644,44 @@
                         'company, '
                         'COUNT(1) AS number, '
                         'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._converted_state()) + ') '
+                        for x in cls._converted_state()) + ') '
                         'THEN 1 ELSE 0 END) AS converted, '
                         'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._lost_state()) + ') '
+                        for x in cls._lost_state()) + ') '
                         'THEN 1 ELSE 0 END) AS lost, '
                         'SUM(amount) AS amount, '
                         'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._converted_state()) + ') '
+                        for x in cls._converted_state()) + ') '
                         'THEN amount ELSE 0 END) AS converted_amount '
-                    'FROM "' + opportunity_obj._table + '" '
-                    'GROUP BY year, month, company) AS "' + self._table + '"',
+                    'FROM "' + Opportunity._table + '" '
+                    'GROUP BY year, month, company) AS "' + cls._table + '"',
                 [])
 
-    def get_conversion_rate(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            if record.number:
-                res[record.id] = (float(record.converted)
-                    / record.number * 100.0)
-            else:
-                res[record.id] = 0.0
-        return res
+    def get_conversion_rate(self, name):
+        if self.number:
+            return float(self.converted) / self.number * 100.0
+        else:
+            return 0.0
 
-    def get_year_month(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            res[record.id] = '%s-%s' % (record.year, int(record.month))
-        return res
+    def get_year_month(self, name):
+        return '%s-%s' % (self.year, int(self.month))
 
-    def get_currency(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            res[record.id] = record.company.currency.id
-        return res
+    def get_currency(self, name):
+        return self.company.currency.id
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            res[record.id] = record.company.currency.digits
-        return res
+    def get_currency_digits(self, name):
+        return self.company.currency.digits
 
-    def get_conversion_amount_rate(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            if record.amount:
-                res[record.id] = (float(record.converted_amount) /
-                        float(record.amount) * 100.0)
-            else:
-                res[record.id] = 0.0
-        return res
-
-SaleOpportunityMonthly()
+    def get_conversion_amount_rate(self, name):
+        if self.amount:
+            return float(self.converted_amount) / float(self.amount) * 100.0
+        else:
+            return 0.0
 
 
 class SaleOpportunityEmployeeMonthly(ModelSQL, ModelView):
     'Sale Opportunity per Employee per Month'
-    _name = 'sale.opportunity_employee_monthly'
-    _description = __doc__
-
+    __name__ = 'sale.opportunity_employee_monthly'
     year = fields.Char('Year')
     month = fields.Integer('Month')
     employee = fields.Many2One('company.employee', 'Employee')
@@ -788,22 +703,26 @@
     conversion_amount_rate = fields.Function(fields.Float(
         'Conversion Amount Rate', help='In %'), 'get_conversion_amount_rate')
 
-    def __init__(self):
-        super(SaleOpportunityEmployeeMonthly, self).__init__()
-        self._order.insert(0, ('year', 'DESC'))
-        self._order.insert(1, ('month', 'DESC'))
-        self._order.insert(2, ('employee', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(SaleOpportunityEmployeeMonthly, cls).__setup__()
+        cls._order.insert(0, ('year', 'DESC'))
+        cls._order.insert(1, ('month', 'DESC'))
+        cls._order.insert(2, ('employee', 'ASC'))
 
-    def _converted_state(self):
+    @staticmethod
+    def _converted_state():
         return ['converted']
 
-    def _lost_state(self):
+    @staticmethod
+    def _lost_state():
         return ['lost']
 
-    def table_query(self):
-        opportunity_obj = Pool().get('sale.opportunity')
-        type_id = FIELDS[self.id._type].sql_type(self.id)[0]
-        type_year = FIELDS[self.year._type].sql_type(self.year)[0]
+    @classmethod
+    def table_query(cls):
+        Opportunity = Pool().get('sale.opportunity')
+        type_id = FIELDS[cls.id._type].sql_type(cls.id)[0]
+        type_year = FIELDS[cls.year._type].sql_type(cls.year)[0]
         return ('SELECT CAST(id AS ' + type_id + ') AS id, create_uid, '
                     'create_date, write_uid, write_date, '
                     'CAST(year AS ' + type_year + ') AS year, month, '
@@ -823,49 +742,33 @@
                         'company, '
                         'COUNT(1) AS number, '
                         'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._converted_state()) + ') '
+                        for x in cls._converted_state()) + ') '
                         'THEN 1 ELSE 0 END) AS converted, '
                         'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._lost_state()) + ') '
+                        for x in cls._lost_state()) + ') '
                         'THEN 1 ELSE 0 END) AS lost, '
                         'SUM(amount) AS amount, '
                         'SUM(CASE WHEN state IN (' + ','.join("'%s'" % x
-                        for x in self._converted_state()) + ') '
+                        for x in cls._converted_state()) + ') '
                         'THEN amount ELSE 0 END) AS converted_amount '
-                    'FROM "' + opportunity_obj._table + '" '
+                    'FROM "' + Opportunity._table + '" '
                     'GROUP BY year, month, employee, company) '
-                'AS "' + self._table + '"', [])
+                'AS "' + cls._table + '"', [])
 
-    def get_conversion_rate(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            if record.number:
-                res[record.id] = (float(record.converted)
-                    / record.number * 100.0)
-            else:
-                res[record.id] = 0.0
-        return res
+    def get_conversion_rate(self, name):
+        if self.number:
+            return float(self.converted) / self.number * 100.0
+        else:
+            return 0.0
 
-    def get_currency(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            res[record.id] = record.company.currency.id
-        return res
+    def get_currency(self, name):
+        return self.company.currency.id
 
-    def get_currency_digits(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            res[record.id] = record.company.currency.digits
-        return res
+    def get_currency_digits(self, name):
+        return self.company.currency.digits
 
-    def get_conversion_amount_rate(self, ids, name):
-        res = {}
-        for record in self.browse(ids):
-            if record.amount:
-                res[record.id] = (float(record.converted_amount) /
-                        float(record.amount) * 100.0)
-            else:
-                res[record.id] = 0.0
-        return res
-
-SaleOpportunityEmployeeMonthly()
+    def get_conversion_amount_rate(self, name):
+        if self.amount:
+            return float(self.converted_amount) / float(self.amount) * 100.0
+        else:
+            return 0.0
diff -r c062b0ae96c6 trytond/modules/sale_price_list/__init__.py
--- a/trytond/modules/sale_price_list/__init__.py	Fri Jun 08 19:34:24 2012 +0200
+++ b/trytond/modules/sale_price_list/__init__.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,6 +1,16 @@
 #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.pool import Pool
 from .party import *
 from .sale import *
 from .product import *
+
+
+def register():
+    Pool.register(
+        Party,
+        Sale,
+        SaleLine,
+        Product,
+        module='sale_price_list', type_='model')
diff -r c062b0ae96c6 trytond/modules/sale_price_list/party.py
--- a/trytond/modules/sale_price_list/party.py	Fri Jun 08 19:34:24 2012 +0200
+++ b/trytond/modules/sale_price_list/party.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,19 +1,20 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from trytond.model import ModelView, ModelSQL, fields
-from trytond.pyson import Eval, Bool, Not, Get
+from trytond.model import fields
+from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 
+__all__ = ['Party']
+__metaclass__ = PoolMeta
 
-class Party(ModelSQL, ModelView):
-    _name = 'party.party'
 
+class Party:
+    __name__ = 'party.party'
     sale_price_list = fields.Property(fields.Many2One('product.price_list',
             'Sale Price List',
             domain=[
-                ('company', '=', Get(Eval('context', {}), 'company')),
+                ('company', '=', Eval('context', {}).get('company')),
                 ],
             states={
-                'invisible': Not(Bool(Get(Eval('context', {}), 'company'))),
+                'invisible': ~Eval('context', {}).get('company'),
                 }))
-
-Party()
diff -r c062b0ae96c6 trytond/modules/sale_price_list/product.py
--- a/trytond/modules/sale_price_list/product.py	Fri Jun 08 19:34:24 2012 +0200
+++ b/trytond/modules/sale_price_list/product.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,25 +1,33 @@
 #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 ModelView, ModelSQL
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Product']
+__metaclass__ = PoolMeta
 
-class Product(ModelSQL, ModelView):
-    _name = 'product.product'
 
-    def get_sale_price(self, ids, quantity=0):
-        price_list_obj = Pool().get('product.price_list')
+class Product:
+    __name__ = 'product.product'
 
-        res = super(Product, self).get_sale_price(ids, quantity=quantity)
+    @classmethod
+    def get_sale_price(cls, products, quantity=0):
+        pool = Pool()
+        PriceList = pool.get('product.price_list')
+        Party = pool.get('party.party')
+        Uom = pool.get('product.uom')
+
+        prices = super(Product, cls).get_sale_price(products,
+            quantity=quantity)
         if (Transaction().context.get('price_list')
                 and Transaction().context.get('customer')):
-            for product in self.browse(ids):
-                res[product.id] = price_list_obj.compute(
-                        Transaction().context['price_list'],
-                        Transaction().context['customer'],
-                        product, res[product.id], quantity,
-                        Transaction().context.get('uom', product.default_uom))
-        return res
-
-Product()
+            price_list = PriceList(Transaction().context['price_list'])
+            customer = Party(Transaction().context['customer'])
+            context_uom = None
+            if Transaction().context.get('uom'):
+                context_uom = Uom(Transaction().context['uom'])
+            for product in products:
+                uom = context_uom or product.default_uom
+                prices[product.id] = price_list.compute(customer, product,
+                    product, quantity, uom)
+        return prices
diff -r c062b0ae96c6 trytond/modules/sale_price_list/sale.py
--- a/trytond/modules/sale_price_list/sale.py	Fri Jun 08 19:34:24 2012 +0200
+++ b/trytond/modules/sale_price_list/sale.py	Mon Sep 24 20:36:42 2012 +0200
@@ -3,12 +3,14 @@
 import copy
 from trytond.model import Model, fields
 from trytond.pyson import Eval, Not, Equal, Or, Bool
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Sale', 'SaleLine']
+__metaclass__ = PoolMeta
 
-class Sale(Model):
-    _name = 'sale.sale'
 
+class Sale:
+    __name__ = 'sale.sale'
     price_list = fields.Many2One('product.price_list', 'Price List',
         domain=[('company', '=', Eval('company'))],
         states={
@@ -17,64 +19,42 @@
             },
         depends=['state', 'company', 'lines'])
 
-    def __init__(self):
-        super(Sale, self).__init__()
-        self.party = copy.copy(self.party)
-        self.party.states = copy.copy(self.party.states)
-        self.party.states['readonly'] = Or(self.party.states['readonly'],
-                Bool(Eval('lines')))
-        if 'lines' not in self.party.depends:
-            self.party.depends = copy.copy(self.party.depends)
-            self.party.depends.append('lines')
-        self.lines = copy.copy(self.lines)
-        self.lines.states = copy.copy(self.lines.states)
-        self.lines.states['readonly'] = Or(self.lines.states['readonly'],
-                Not(Bool(Eval('party'))))
-        if 'party' not in self.lines.depends:
-            self.lines.depends = copy.copy(self.lines.depends)
-            self.lines.depends.append('party')
-        self._reset_columns()
+    @classmethod
+    def __setup__(cls):
+        super(Sale, cls).__setup__()
+        cls.party.states['readonly'] = (cls.party.states['readonly']
+            | Eval('lines'))
+        if 'lines' not in cls.party.depends:
+            cls.party.depends.append('lines')
+        cls.lines.states['readonly'] = (cls.lines.states['readonly']
+            | ~Eval('party'))
+        if 'party' not in cls.lines.depends:
+            cls.lines.depends.append('party')
 
-    def on_change_party(self, values):
-        party_obj = Pool().get('party.party')
-        price_list_obj = Pool().get('product.price_list')
-        res = super(Sale, self).on_change_party(values)
+    def on_change_party(self):
+        res = super(Sale, self).on_change_party()
         res['price_list'] = None
-        if values.get('party'):
-            party = party_obj.browse(values['party'])
-            res['price_list'] = party.sale_price_list and \
-                    party.sale_price_list.id or None
-        if res['price_list']:
-            res['price_list.rec_name'] = price_list_obj.browse(
-                    res['price_list']).rec_name
+        if self.party and self.party.sale_price_list:
+            res['price_list'] = self.party.sale_price_list.id
+            res['price_list.rec_name'] = self.party.sale_price_list.rec_name
         return res
 
-Sale()
 
+class SaleLine:
+    __name__ = 'sale.line'
 
-class SaleLine(Model):
-    _name = 'sale.line'
+    @classmethod
+    def __setup__(cls):
+        super(SaleLine, cls).__setup__()
+        if '_parent_sale.price_list' not in cls.quantity.on_change:
+            cls.quantity.on_change.append('_parent_sale.price_list')
+        if '_parent_sale.price_list' not in cls.unit.on_change:
+            cls.unit.on_change.append('_parent_sale.price_list')
+        if '_parent_sale.price_list' not in cls.product.on_change:
+            cls.product.on_change.append('_parent_sale.price_list')
 
-    def __init__(self):
-        super(SaleLine, self).__init__()
-        if '_parent_sale.price_list' not in self.quantity.on_change:
-            self.quantity = copy.copy(self.quantity)
-            self.quantity.on_change = copy.copy(self.quantity.on_change)
-            self.quantity.on_change.append('_parent_sale.price_list')
-        if '_parent_sale.price_list' not in self.unit.on_change:
-            self.unit = copy.copy(self.unit)
-            self.unit.on_change = copy.copy(self.unit.on_change)
-            self.unit.on_change.append('_parent_sale.price_list')
-        if '_parent_sale.price_list' not in self.product.on_change:
-            self.product = copy.copy(self.product)
-            self.product.on_change = copy.copy(self.product.on_change)
-            self.product.on_change.append('_parent_sale.price_list')
-        self._reset_columns()
-
-    def _get_context_sale_price(self, product, vals):
-        res = super(SaleLine, self)._get_context_sale_price(product, vals)
-        if vals.get('_parent_sale.price_list'):
-            res['price_list'] = vals['_parent_sale.price_list']
-        return res
-
-SaleLine()
+    def _get_context_sale_price(self):
+        context = super(SaleLine, self)._get_context_sale_price()
+        if self.sale and getattr(self.sale, 'price_list', None):
+            context['price_list'] = self.sale.price_list.id
+        return context
diff -r 2c8f8f0a1002 trytond/modules/sale_shipment_cost/__init__.py
--- a/trytond/modules/sale_shipment_cost/__init__.py	Tue Sep 11 15:28:58 2012 +0200
+++ b/trytond/modules/sale_shipment_cost/__init__.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,5 +1,15 @@
 #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.pool import Pool
 from .sale import *
 from .stock import *
+
+
+def register():
+    Pool.register(
+        Configuration,
+        Sale,
+        SaleLine,
+        ShipmentOut,
+        module='sale_shipment_cost', type_='model')
diff -r 2c8f8f0a1002 trytond/modules/sale_shipment_cost/sale.py
--- a/trytond/modules/sale_shipment_cost/sale.py	Tue Sep 11 15:28:58 2012 +0200
+++ b/trytond/modules/sale_shipment_cost/sale.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,15 +1,16 @@
 #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 copy
-from trytond.model import Model, fields
+from trytond.model import fields
 from trytond.transaction import Transaction
 from trytond.pyson import Eval, Bool
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Configuration', 'Sale', 'SaleLine']
+__metaclass__ = PoolMeta
 
-class Configuration(Model):
-    _name = 'sale.configuration'
 
+class Configuration:
+    __name__ = 'sale.configuration'
     sale_carrier = fields.Property(fields.Many2One('carrier', 'Carrier',
         domain=[('carrier_product.salable', '=', True)],
     ))
@@ -22,16 +23,13 @@
                 },
             depends=['company']))
 
-    def default_sale_shipment_cost_method(self):
+    @staticmethod
+    def default_sale_shipment_cost_method():
         return 'shipment'
 
 
-Configuration()
-
-
-class Sale(Model):
-    _name = 'sale.sale'
-
+class Sale:
+    __name__ = 'sale.sale'
     carrier = fields.Many2One('carrier', 'Carrier',
         domain=[('carrier_product.salable', '=', True)],
         on_change=['carrier', 'party', 'currency', 'sale_date'],
@@ -46,82 +44,73 @@
             'readonly': Eval('state') != 'draft',
             }, depends=['state'])
 
-    def __init__(self):
-        super(Sale, self).__init__()
+    @classmethod
+    def __setup__(cls):
+        super(Sale, cls).__setup__()
 
-        self.lines = copy.copy(self.lines)
-        self.lines.on_change = copy.copy(self.lines.on_change)
         for fname in ('carrier', 'party', 'currency', 'sale_date',
                 'shipment_cost_method', 'lines'):
-            if fname not in self.lines.on_change:
-                self.lines.on_change.append(fname)
-        self.carrier = copy.copy(self.carrier)
-        self.carrier.on_change = copy.copy(self.carrier.on_change)
-        for fname in self.lines.on_change:
-            if fname not in self.carrier.on_change:
-                self.carrier.on_change.append(fname)
-        self._reset_columns()
+            if fname not in cls.lines.on_change:
+                cls.lines.on_change.append(fname)
+        for fname in cls.lines.on_change:
+            if fname not in cls.carrier.on_change:
+                cls.carrier.on_change.append(fname)
 
-    def default_carrier(self):
-        config_obj = Pool().get('sale.configuration')
-        config = config_obj.browse(1)
-        return config.sale_carrier.id
+    @staticmethod
+    def default_carrier():
+        Config = Pool().get('sale.configuration')
+        config = Config(1)
+        return config.sale_carrier.id if config.sale_carrier else None
 
-    def default_shipment_cost_method(self):
-        config_obj = Pool().get('sale.configuration')
-        config = config_obj.browse(1)
+    @staticmethod
+    def default_shipment_cost_method():
+        Config = Pool().get('sale.configuration')
+        config = Config(1)
         return config.sale_shipment_cost_method
 
-    def _get_carrier_context(self, values):
+    def _get_carrier_context(self):
         return {}
 
-    def on_change_carrier(self, values):
-        return self.on_change_lines(values)
+    def on_change_carrier(self):
+        return self.on_change_lines()
 
-    def on_change_lines(self, values):
+    def on_change_lines(self):
         pool = Pool()
-        carrier_obj = pool.get('carrier')
-        party_obj = pool.get('party.party')
-        product_obj = pool.get('product.product')
-        currency_obj = pool.get('currency.currency')
-        sale_line_obj = pool.get('sale.line')
-        tax_rule_obj = pool.get('account.tax.rule')
-        date_obj = pool.get('ir.date')
+        Product = pool.get('product.product')
+        Currency = pool.get('currency.currency')
+        SaleLine = pool.get('sale.line')
+        Date = pool.get('ir.date')
 
-        today = date_obj.today()
+        today = Date.today()
 
         cost, currency_id = 0, None
-        if values.get('carrier'):
-            carrier = carrier_obj.browse(values['carrier'])
-            with Transaction().set_context(
-                self._get_carrier_context(values)):
-                cost, currency_id = carrier_obj.get_sale_price(carrier)
+        if self.carrier:
+            with Transaction().set_context(self._get_carrier_context()):
+                cost, currency_id = self.carrier.get_sale_price()
 
         party = None
         party_context = {}
-        if values.get('party'):
-            party = party_obj.browse(values['party'])
-            if party.lang:
-                party_context['language'] = party.lang.code
+        if self.party:
+            if self.party.lang:
+                party_context['language'] = self.party.lang.code
 
         cost_line = {}
-        product_ids = [line['product'] for line in values.get('lines') or []
-                if line.get('product')]
+        products = [line.product for line in self.lines or []
+                if line.product]
         stockable = any(product.type in ('goods', 'assets')
-            for product in product_obj.browse(product_ids))
+            for product in products)
         if cost and currency_id and stockable:
-            if (values.get('currency')
-                    and currency_id != values['currency']):
-                date = values.get('sale_date') or today
+            if (self.currency
+                    and currency_id != self.currency.id):
+                date = self.sale_date or today
                 with Transaction().set_context(date=date):
-                    cost = currency_obj.compute(currency_id, cost,
-                        values['currency'])
-            product = carrier.carrier_product
+                    cost = Currency.compute(Currency(currency_id), cost,
+                        self.currency)
+            product = self.carrier.carrier_product
             with Transaction().set_context(party_context):
-                description = product_obj.browse(product.id).rec_name
+                description = Product(product.id).rec_name
             taxes = []
-            cost_line = sale_line_obj.default_get(
-                sale_line_obj._columns.keys())
+            cost_line = SaleLine.default_get(SaleLine._fields.keys())
             cost_line.update({
                     'type': 'line',
                     'product': product.id,
@@ -134,44 +123,37 @@
                     'taxes': taxes,
                     'sequence': 9999,  # XXX
                     })
-            pattern = sale_line_obj._get_tax_rule_pattern(party,
-                cost_line)
+            pattern = SaleLine(**cost_line)._get_tax_rule_pattern()
             for tax in product.customer_taxes_used:
                 if party and party.customer_tax_rule:
-                    tax_ids = tax_rule_obj.apply(party.customer_tax_rule,
-                        tax, pattern)
+                    tax_ids = party.customer_tax_rule.apply(tax, pattern)
                     if tax_ids:
                         taxes.extend(tax_ids)
                     continue
                 taxes.append(tax.id)
             if party and party.customer_tax_rule:
-                tax_ids = tax_rule_obj.apply(party.customer_tax_rule, None,
-                    pattern)
+                tax_ids = party.customer_tax_rule.apply(None, pattern)
                 if tax_ids:
                     taxes.extend(tax_ids)
 
         to_remove = None
         operator, operand = None, None
-        values = values.copy()
-        if not values.get('lines'):
-            values['lines'] = []
-        for line in values['lines']:
-            if line.get('shipment_cost'):
-                if line['shipment_cost'] != cost_line.get('shipment_cost'):
-                    to_remove = line['id']
-                    values['lines'].remove(line)
+        if not self.lines:
+            self.lines = []
+        for line in self.lines:
+            if line.shipment_cost:
+                if line.shipment_cost != cost_line.get('shipment_cost'):
+                    to_remove = line.id
+                    self.lines.remove(line)
                     if cost_line:
-                        del cost_line['description']
-                        line.update(cost_line)
-                        cost_line = line
-                        del cost_line['id']
+                        cost_line['description'] = line.description
                 else:
                     cost_line = {}
                 break
         if cost_line:
-            values['lines'].append(cost_line)
+            self.lines.append(SaleLine(**cost_line))
 
-        result = super(Sale, self).on_change_lines(values)
+        result = super(Sale, self).on_change_lines()
 
         lines = result.setdefault('lines', {})
         if to_remove:
@@ -180,76 +162,62 @@
             lines.setdefault('add', []).append(cost_line)
         return result
 
-    def create_shipment(self, sale, shipment_type):
-        pool = Pool()
-        shipment_obj = pool.get('stock.shipment.out')
-        carrier_obj = pool.get('carrier')
+    def create_shipment(self, shipment_type):
+        Shipment = Pool().get('stock.shipment.out')
 
-        shipment_ids = super(Sale, self).create_shipment(sale, shipment_type)
-        if shipment_type == 'out' and shipment_ids and sale.carrier:
-            shipments = shipment_obj.browse(shipment_ids)
+        shipments = super(Sale, self).create_shipment(shipment_type)
+        if shipment_type == 'out' and shipments and self.carrier:
             for shipment in shipments:
+                shipment.carrier = self.carrier
                 with Transaction().set_context(
-                        shipment_obj.get_carrier_context(shipment, values={
-                                'carrier': sale.carrier.id,
-                                })):
-                    cost, currency_id = carrier_obj.get_sale_price(
-                        sale.carrier)
+                        shipment.get_carrier_context()):
+                    cost, currency_id = self.carrier.get_sale_price()
                 with Transaction().set_user(0, set_context=True):
-                    shipment_obj.write(shipment.id, {
-                        'carrier': sale.carrier.id,
-                        'cost': cost,
-                        'cost_currency': currency_id,
-                    })
-        return shipment_ids
+                    Shipment.write([shipment], {
+                            'carrier': self.carrier.id,
+                            'cost': cost,
+                            'cost_currency': currency_id,
+                            })
+        return shipments
 
-    def _get_invoice_line_sale_line(self, sale, invoice_type):
-        result = super(Sale, self)._get_invoice_line_sale_line(sale,
-            invoice_type)
-        if sale.shipment_cost_method == 'shipment':
-            for line in sale.lines:
+    def _get_invoice_line_sale_line(self, invoice_type):
+        result = super(Sale, self)._get_invoice_line_sale_line(invoice_type)
+        if self.shipment_cost_method == 'shipment':
+            for line in self.lines:
                 if line.id in result and line.shipment_cost:
                     del result[line.id]
         return result
 
-    def create_invoice(self, sale, invoice_type):
+    def create_invoice(self, invoice_type):
         pool = Pool()
-        invoice_obj = pool.get('account.invoice')
-        invoice_line_obj = pool.get('account.invoice.line')
-        shipment_obj = pool.get('stock.shipment.out')
+        Invoice = pool.get('account.invoice')
+        Shipment = pool.get('stock.shipment.out')
 
-        invoice_id = super(Sale, self).create_invoice(sale, invoice_type)
-        if (invoice_id
+        invoice = super(Sale, self).create_invoice(invoice_type)
+        if (invoice
                 and invoice_type == 'out_invoice'
-                and sale.shipment_cost_method == 'shipment'):
+                and self.shipment_cost_method == 'shipment'):
             with Transaction().set_user(0, set_context=True):
-                invoice = invoice_obj.browse(invoice_id)
-            for shipment in sale.shipments:
+                invoice = Invoice(invoice.id)
+            for shipment in self.shipments:
                 if (shipment.state == 'done'
                         and shipment.carrier
                         and shipment.cost
                         and not shipment.cost_invoice_line):
-                    vals = shipment_obj.get_cost_invoice_line(
-                            shipment, invoice)
-                    if not vals:
+                    invoice_line = shipment.get_cost_invoice_line(invoice)
+                    if not invoice_line:
                         continue
-                    vals['invoice'] = invoice_id
-                    with Transaction().set_user(0, set_context=True):
-                        invoice_line_id = invoice_line_obj.create(vals)
-                    shipment_obj.write(shipment.id, {
-                        'cost_invoice_line': invoice_line_id,
-                        })
+                    invoice_line.invoice = invoice
+                    invoice_line.save()
+                    Shipment.write([shipment], {
+                            'cost_invoice_line': invoice_line.id,
+                            })
             with Transaction().set_user(0, set_context=True):
-                invoice_obj.update_taxes([invoice_id])
-        return invoice_id
+                Invoice.update_taxes([invoice])
+        return invoice
 
-Sale()
 
-
-class SaleLine(Model):
-    _name = 'sale.line'
-
+class SaleLine:
+    __name__ = 'sale.line'
     shipment_cost = fields.Numeric('Shipment Cost',
         digits=(16, Eval('_parent_sale', {}).get('currency_digits', 2)))
-
-SaleLine()
diff -r 2c8f8f0a1002 trytond/modules/sale_shipment_cost/stock.py
--- a/trytond/modules/sale_shipment_cost/stock.py	Tue Sep 11 15:28:58 2012 +0200
+++ b/trytond/modules/sale_shipment_cost/stock.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,15 +1,16 @@
 #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 copy
-from trytond.model import Model, fields
+from trytond.model import fields
 from trytond.pyson import Eval, Bool
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['ShipmentOut']
+__metaclass__ = PoolMeta
 
-class ShipmentOut(Model):
-    _name = 'stock.shipment.out'
 
+class ShipmentOut:
+    __name__ = 'stock.shipment.out'
     carrier = fields.Many2One('carrier', 'Carrier', states={
             'readonly': Eval('state') != 'draft',
             }, on_change=['carrier'],
@@ -23,7 +24,7 @@
             }, depends=['carrier', 'state'])
     cost_currency_digits = fields.Function(fields.Integer(
         'Cost Currency Digits', on_change_with=['currency']),
-        'get_cost_currency_digits')
+        'on_change_with_cost_currency_digits')
     cost = fields.Numeric('Cost',
             digits=(16, Eval('cost_currency_digits', 2)), states={
             'invisible': ~Eval('carrier'),
@@ -33,129 +34,99 @@
     cost_invoice_line = fields.Many2One('account.invoice.line',
             'Cost Invoice Line', readonly=True)
 
-    def __init__(self):
-        super(ShipmentOut, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentOut, cls).__setup__()
+        cls._error_messages.update({
             'missing_account_revenue': 'It misses '
                     'an "Account Revenue" on product "%s"!',
             })
-        self.inventory_moves = copy.copy(self.inventory_moves)
-        if not self.inventory_moves.on_change:
-            self.inventory_moves.on_change = []
-        else:
-            self.inventory_moves.on_change = copy.copy(
-                self.inventory_moves.on_change)
+        if not cls.inventory_moves.on_change:
+            cls.inventory_moves.on_change = []
         for fname in ('carrier', 'customer', 'inventory_moves'):
-            if fname not in self.inventory_moves.on_change:
-                self.inventory_moves.on_change.append(fname)
-        self._rpc.setdefault('on_change_inventory_moves', False)
-        self.carrier = copy.copy(self.carrier)
-        self.carrier.on_change = copy.copy(self.carrier.on_change)
-        for fname in self.inventory_moves.on_change:
-            if fname not in self.carrier.on_change:
-                self.carrier.on_change.append(fname)
-        self._reset_columns()
+            if fname not in cls.inventory_moves.on_change:
+                cls.inventory_moves.on_change.append(fname)
+        for fname in cls.inventory_moves.on_change:
+            if fname not in cls.carrier.on_change:
+                cls.carrier.on_change.append(fname)
 
-    def on_change_with_cost_currency_digits(self, values):
-        currency_obj = Pool().get('currency.currency')
-        if values.get('currency'):
-            currency = currency_obj.browse(values['currency'])
-            return currency.digits
+    def on_change_with_cost_currency_digits(self, name=None):
+        if self.cost_currency:
+            return self.cost_currency.digits
         return 2
 
-    def get_cost_currency_digits(self, ids, name):
-        '''
-        Return the number of digits of the cost currency
-        '''
-        result = {}
-        for shipment in self.browse(ids):
-            if shipment.cost_currency:
-                result[shipment.id] = shipment.cost_currency.digits
-            else:
-                result[shipment.id] = 2
-        return result
-
-    def _get_carrier_context(self, values):
+    def _get_carrier_context(self):
         return {}
 
-    def get_carrier_context(self, shipment, values=None):
-        if values is None:
-            values = {}
-        return self._get_carrier_context(values)
+    def get_carrier_context(self):
+        return self._get_carrier_context()
 
-    def on_change_carrier(self, values):
-        return self.on_change_inventory_moves(values)
+    def on_change_carrier(self):
+        return self.on_change_inventory_moves()
 
-    def on_change_inventory_moves(self, values):
-        pool = Pool()
-        carrier_obj = pool.get('carrier')
-        currency_obj = pool.get('currency.currency')
+    def on_change_inventory_moves(self):
+        Currency = Pool().get('currency.currency')
 
         try:
-            result = super(ShipmentOut, self).on_change_inventory_moves(values)
+            result = super(ShipmentOut, self).on_change_inventory_moves()
         except AttributeError:
             result = {}
-        if not values.get('carrier'):
+        if not self.carrier:
             return result
-        carrier = carrier_obj.browse(values['carrier'])
-        with Transaction().set_context(
-                self._get_carrier_context(values)):
-            cost, currency_id = carrier_obj.get_sale_price(carrier)
-        currency = currency_obj.browse(currency_id)
+        with Transaction().set_context(self._get_carrier_context()):
+            cost, currency_id = self.carrier.get_sale_price()
+        currency = Currency(currency_id)
         result['cost'] = cost
-        result['cost_currency'] = currency_id
+        result['cost_currency'] = currency.id
         result['cost_currency_digits'] = currency.digits if currency else 2
         return result
 
-    def _get_cost_tax_rule_pattern(self, shipment):
+    def _get_cost_tax_rule_pattern(self):
         'Get tax rule pattern for invoice line'
         return {}
 
-    def get_cost_invoice_line(self, shipment, invoice):
+    def get_cost_invoice_line(self, invoice):
         pool = Pool()
-        product_obj = pool.get('product.product')
-        tax_rule_obj = pool.get('account.tax.rule')
-        currency_obj = pool.get('currency.currency')
+        Product = pool.get('product.product')
+        Currency = pool.get('currency.currency')
+        InvoiceLine = pool.get('account.invoice.line')
 
-        if not shipment.cost:
+        if not self.cost:
             return {}
-        values = {}
-        product = shipment.carrier.carrier_product
-        values['type'] = 'line'
+        with Transaction().set_user(0, set_context=True):
+            invoice_line = InvoiceLine()
+        product = self.carrier.carrier_product
+        invoice_line.type = 'line'
 
         party = invoice.party
         party_context = {}
         if party.lang:
             party_context['language'] = party.lang.code
         with Transaction().set_context(party_context):
-            values['description'] = product_obj.browse(product.id).rec_name
+            invoice_line.description = Product(product.id).rec_name
 
-        values['quantity'] = 1  # XXX
-        values['unit'] = product.sale_uom.id
-        cost = shipment.cost
-        if invoice.currency != shipment.cost_currency:
+        invoice_line.quantity = 1  # XXX
+        invoice_line.unit = product.sale_uom.id
+        cost = self.cost
+        if invoice.currency != self.cost_currency:
             with Transaction().set_context(date=invoice.currency_date):
-                cost = currency_obj.compute(
-                    shipment.cost_currency.id,
-                    cost, invoice.currency.id)
-        values['unit_price'] = cost
+                cost = Currency.compute(self.cost_currency, cost,
+                    invoice.currency)
+        invoice_line.unit_price = cost
 
         taxes = []
-        pattern = self._get_cost_tax_rule_pattern(shipment)
+        pattern = self._get_cost_tax_rule_pattern()
         for tax in product.customer_taxes_used:
             if party.customer_tax_rule:
-                tax_ids = tax_rule_obj.apply(party.customer_tax_rule, None,
-                        pattern)
+                tax_ids = party.customer_tax_rule.apply(None, pattern)
                 if tax_ids:
                     taxes.extend(tax_ids)
                 continue
             taxes.append(tax.id)
-        values['taxes'] = [('set', taxes)]
+        invoice_line.taxes = taxes
 
-        values['account'] = product.account_revenue_used.id
-        if not values['account']:
+        invoice_line.account = product.account_revenue_used
+        if not invoice_line.account:
             self.raise_user_error('missing_account_revenue',
                     error_args=(product.rec_name,))
-        return values
-
-ShipmentOut()
+        return invoice_line
diff -r 091f89c47ad9 trytond/modules/stock/__init__.py
--- a/trytond/modules/stock/__init__.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/__init__.py	Mon Sep 24 20:36:42 2012 +0200
@@ -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 trytond.pool import Pool
 from .location import *
 from .shipment import *
 from .period import *
@@ -8,3 +9,47 @@
 from .product import *
 from .inventory import *
 from .configuration import *
+
+
+def register():
+    Pool.register(
+        Location,
+        Party,
+        ProductsByLocationsStart,
+        ShipmentIn,
+        ShipmentInReturn,
+        ShipmentOut,
+        ShipmentOutReturn,
+        AssignShipmentOutAssignFailed,
+        ShipmentInternal,
+        Address,
+        AssignShipmentInternalAssignFailed,
+        AssignShipmentInReturnAssignFailed,
+        Period,
+        Cache,
+        Move,
+        Template,
+        Product,
+        ProductByLocationStart,
+        ProductQuantitiesByWarehouse,
+        ProductQuantitiesByWarehouseStart,
+        Inventory,
+        InventoryLine,
+        Configuration,
+        module='stock', type_='model')
+    Pool.register(
+        ProductsByLocations,
+        AssignShipmentOut,
+        AssignShipmentInternal,
+        AssignShipmentInReturn,
+        CreateShipmentOutReturn,
+        ProductByLocation,
+        OpenProductQuantitiesByWarehouse,
+        module='stock', type_='wizard')
+    Pool.register(
+        DeliveryNote,
+        PickingList,
+        SupplierRestockingList,
+        CustomerReturnRestockingList,
+        InteralShipmentReport,
+        module='stock', type_='report')
diff -r 091f89c47ad9 trytond/modules/stock/configuration.py
--- a/trytond/modules/stock/configuration.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/configuration.py	Mon Sep 24 20:36:42 2012 +0200
@@ -3,12 +3,12 @@
 from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
 from trytond.pyson import Eval, Get
 
+__all__ = ['Configuration']
+
 
 class Configuration(ModelSingleton, ModelSQL, ModelView):
     'Stock Configuration'
-    _name = 'stock.configuration'
-    _description = __doc__
-
+    __name__ = 'stock.configuration'
     shipment_in_sequence = fields.Property(fields.Many2One('ir.sequence',
             'Supplier Shipment Sequence', domain=[
                 ('company', 'in',
@@ -39,5 +39,3 @@
                     [Get(Eval('context', {}), 'company'), None]),
                 ('code', '=', 'stock.shipment.internal'),
                 ], required=True))
-
-Configuration()
diff -r 091f89c47ad9 trytond/modules/stock/inventory.py
--- a/trytond/modules/stock/inventory.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/inventory.py	Mon Sep 24 20:36:42 2012 +0200
@@ -6,6 +6,8 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Inventory', 'InventoryLine']
+
 STATES = {
     'readonly': Not(Equal(Eval('state'), 'draft')),
 }
@@ -14,9 +16,7 @@
 
 class Inventory(Workflow, ModelSQL, ModelView):
     'Stock Inventory'
-    _name = 'stock.inventory'
-    _description = __doc__
-
+    __name__ = 'stock.inventory'
     location = fields.Many2One(
         'stock.location', 'Location', required=True,
         domain=[('type', '=', 'storage')], states={
@@ -47,18 +47,19 @@
         ('cancel', 'Canceled'),
         ], 'State', readonly=True, select=True)
 
-    def __init__(self):
-        super(Inventory, self).__init__()
-        self._order.insert(0, ('date', 'DESC'))
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Inventory, cls).__setup__()
+        cls._order.insert(0, ('date', 'DESC'))
+        cls._error_messages.update({
                 'delete_cancel': 'Inventory "%s" must be cancelled before ' \
                     'deletion!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('draft', 'done'),
                 ('draft', 'cancel'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'confirm': {
                     'invisible': Eval('state').in_(['done', 'cancel']),
                     },
@@ -70,112 +71,103 @@
                     },
                 })
 
-    def init(self, module_name):
-        super(Inventory, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Inventory, cls).__register__(module_name)
         cursor = Transaction().cursor
 
         # Add index on create_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action('create_date', action='add')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
+    @staticmethod
+    def default_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_lost_found(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(self.lost_found.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_lost_found(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.lost_found.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, inventories):
         # Cancel before delete
-        self.cancel(ids)
-        for inventory in self.browse(ids):
+        cls.cancel(inventories)
+        for inventory in inventories:
             if inventory.state != 'cancel':
-                self.raise_user_error('delete_cancel', inventory.rec_name)
-        return super(Inventory, self).delete(ids)
+                cls.raise_user_error('delete_cancel', inventory.rec_name)
+        super(Inventory, cls).delete(inventories)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('done')
-    def confirm(self, ids):
-        line_obj = Pool().get('stock.inventory.line')
-        for inventory in self.browse(ids):
+    def confirm(self, inventories):
+        for inventory in inventories:
             for line in inventory.lines:
-                line_obj.create_move(line)
+                line.create_move()
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        line_obj = Pool().get("stock.inventory.line")
-        inventories = self.browse(ids)
-        line_obj.cancel_move([l for i in inventories for l in i.lines])
+    def cancel(self, inventories):
+        Line = Pool().get("stock.inventory.line")
+        Line.cancel_move([l for i in inventories for l in i.lines])
 
-    def copy(self, ids, default=None):
-        date_obj = Pool().get('ir.date')
-        line_obj = Pool().get('stock.inventory.line')
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+    @classmethod
+    def copy(cls, inventories, default=None):
+        pool = Pool()
+        Date = pool.get('ir.date')
+        Line = pool.get('stock.inventory.line')
 
         if default is None:
             default = {}
         default = default.copy()
-        default['date'] = date_obj.today()
+        default['date'] = Date.today()
         default['lines'] = None
 
-        new_ids = []
-        for inventory in self.browse(ids):
-            new_id = super(Inventory, self).copy(inventory.id, default=default)
-            line_obj.copy([x.id for x in inventory.lines],
-                    default={
-                        'inventory': new_id,
-                        'move': None,
-                        })
-            self.complete_lines(new_id)
-            new_ids.append(new_id)
+        new_inventories = []
+        for inventory in inventories:
+            new_inventory, = super(Inventory, cls).copy([inventory],
+                default=default)
+            Line.copy(inventory.lines,
+                default={
+                    'inventory': new_inventory.id,
+                    'move': None,
+                    })
+            cls.complete_lines([new_inventory])
+            new_inventories.append(new_inventory)
+        return new_inventories
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-    def complete_lines(self, ids):
+    @classmethod
+    def complete_lines(self, inventories):
         '''
         Complete or update the inventories
-
-        :param ids: the ids of stock.inventory
-        :param context: the context
         '''
         pool = Pool()
-        line_obj = pool.get('stock.inventory.line')
-        product_obj = pool.get('product.product')
-
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        inventories = self.browse(ids)
+        Line = pool.get('stock.inventory.line')
+        Product = pool.get('product.product')
 
         for inventory in inventories:
             # Compute product quantities
             with Transaction().set_context(stock_date_end=inventory.date):
-                pbl = product_obj.products_by_location(
-                        [inventory.location.id])
+                pbl = Product.products_by_location([inventory.location.id])
 
             # Index some data
             product2uom = {}
             product2type = {}
             product2consumable = {}
-            for product in product_obj.browse([line[1] for line in pbl]):
+            for product in Product.browse([line[1] for line in pbl]):
                 product2uom[product.id] = product.default_uom.id
                 product2type[product.id] = product.type
                 product2consumable[product.id] = product.consumable
@@ -189,7 +181,7 @@
                 if not (line.product.active and
                         line.product.type == 'goods'
                         and not line.product.consumable):
-                    line_obj.delete(line.id)
+                    Line.delete([line])
                     continue
                 if line.product.id in product_qty:
                     quantity, uom_id = product_qty.pop(line.product.id)
@@ -197,10 +189,9 @@
                     quantity, uom_id = 0.0, product2uom[line.product.id]
                 else:
                     quantity, uom_id = 0.0, line.product.default_uom.id
-                values = line_obj.update_values4complete(line, quantity,
-                    uom_id)
+                values = line.update_values4complete(quantity, uom_id)
                 if values:
-                    line_obj.write(line.id, values)
+                    Line.write([line], values)
 
             # Create lines if needed
             for product_id in product_qty:
@@ -208,19 +199,15 @@
                         and not product2consumable[product_id]):
                     continue
                 quantity, uom_id = product_qty[product_id]
-                values = line_obj.create_values4complete(product_id, inventory,
-                        quantity, uom_id)
-                line_obj.create(values)
-
-Inventory()
+                values = Line.create_values4complete(product_id, inventory,
+                    quantity, uom_id)
+                Line.create(values)
 
 
 class InventoryLine(ModelSQL, ModelView):
     'Stock Inventory Line'
-    _name = 'stock.inventory.line'
-    _description = __doc__
+    __name__ = 'stock.inventory.line'
     _rec_name = 'product'
-
     product = fields.Many2One('product.product', 'Product', required=True,
         domain=[
             ('type', '=', 'goods'),
@@ -239,121 +226,104 @@
     inventory = fields.Many2One('stock.inventory', 'Inventory', required=True,
             ondelete='CASCADE')
 
-    def __init__(self):
-        super(InventoryLine, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(InventoryLine, cls).__setup__()
+        cls._sql_constraints += [
             ('check_line_qty_pos',
                 'CHECK(quantity >= 0.0)', 'Line quantity must be positive!'),
             ('inventory_product_uniq', 'UNIQUE(inventory, product)',
                 'Product must be unique by inventory!'),
-        ]
-        self._order.insert(0, ('product', 'ASC'))
+            ]
+        cls._order.insert(0, ('product', 'ASC'))
 
-    def default_unit_digits(self):
+    @staticmethod
+    def default_unit_digits():
         return 2
 
-    def default_expected_quantity(self):
+    @staticmethod
+    def default_expected_quantity():
         return 0.
 
-    def on_change_product(self, vals):
-        product_obj = Pool().get('product.product')
-        res = {}
-        res['unit_digits'] = 2
-        if vals.get('product'):
-            product = product_obj.browse(vals['product'])
-            res['uom'] = product.default_uom.id
-            res['uom.rec_name'] = product.default_uom.rec_name
-            res['unit_digits'] = product.default_uom.digits
-        return res
+    def on_change_product(self):
+        change = {}
+        change['unit_digits'] = 2
+        if self.product:
+            change['uom'] = self.product.default_uom.id
+            change['uom.rec_name'] = self.product.default_uom.rec_name
+            change['unit_digits'] = self.product.default_uom.digits
+        return change
 
-    def get_uom(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            res[line.id] = line.product.default_uom.id
-        return res
+    def get_uom(self, name):
+        return self.product.default_uom.id
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            res[line.id] = line.product.default_uom.digits
-        return res
+    def get_unit_digits(self, name):
+        return self.product.default_uom.digits
 
-    def cancel_move(self, lines):
-        move_obj = Pool().get('stock.move')
-        move_obj.write([l.move.id for l in lines if l.move], {
+    @classmethod
+    def cancel_move(cls, lines):
+        Move = Pool().get('stock.move')
+        Move.write([l.move for l in lines if l.move], {
             'state': 'cancel',
             })
-        move_obj.delete([l.move.id for l in lines if l.move])
-        self.write([l.id for l in lines if l.move], {
+        Move.delete([l.move for l in lines if l.move])
+        cls.write([l for l in lines if l.move], {
             'move': None,
             })
 
-    def create_move(self, line):
+    def create_move(self):
         '''
-        Create move for an inventory line
+        Create move for an inventory line and return id
+        '''
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Uom = pool.get('product.uom')
 
-        :param line: a BrowseRecord of inventory.line
-        :return: the stock.move id or None
-        '''
-        move_obj = Pool().get('stock.move')
-        uom_obj = Pool().get('product.uom')
-
-        delta_qty = uom_obj.compute_qty(line.uom,
-            line.expected_quantity - line.quantity,
-            line.uom)
+        delta_qty = Uom.compute_qty(self.uom,
+            self.expected_quantity - self.quantity,
+            self.uom)
         if delta_qty == 0.0:
             return
-        from_location = line.inventory.location.id
-        to_location = line.inventory.lost_found.id
+        from_location = self.inventory.location.id
+        to_location = self.inventory.lost_found.id
         if delta_qty < 0:
             (from_location, to_location, delta_qty) = \
                 (to_location, from_location, -delta_qty)
 
-        move_id = move_obj.create({
+        move = Move.create({
             'from_location': from_location,
             'to_location': to_location,
             'quantity': delta_qty,
-            'product': line.product.id,
-            'uom': line.uom.id,
-            'company': line.inventory.company.id,
+            'product': self.product.id,
+            'uom': self.uom.id,
+            'company': self.inventory.company.id,
             'state': 'done',
-            'effective_date': line.inventory.date,
+            'effective_date': self.inventory.date,
             })
-        self.write(line.id, {
-            'move': move_id,
-            })
-        return move_id
+        self.move = move
+        self.save()
+        return move.id
 
-    def update_values4complete(self, line, quantity, uom_id):
+    def update_values4complete(self, quantity, uom_id):
         '''
         Return update values to complete inventory
+        '''
+        values = {}
+        # if nothing changed, no update
+        if self.quantity == self.expected_quantity == quantity \
+                and self.uom.id == uom_id:
+            return values
+        values['expected_quantity'] = quantity
+        values['uom'] = uom_id
+        # update also quantity field if not edited
+        if self.quantity == self.expected_quantity:
+            values['quantity'] = max(quantity, 0.0)
+        return values
 
-        :param line: a BrowseRecord of inventory.line
-        :param quantity: the actual product quantity for the inventory location
-        :param uom_id: the UoM id of the product line
-        :return: a dictionary
-        '''
-        res = {}
-        # if nothing changed, no update
-        if line.quantity == line.expected_quantity == quantity \
-                and line.uom.id == uom_id:
-            return {}
-        res['expected_quantity'] = quantity
-        res['uom'] = uom_id
-        # update also quantity field if not edited
-        if line.quantity == line.expected_quantity:
-            res['quantity'] = max(quantity, 0.0)
-        return res
-
-    def create_values4complete(self, product_id, inventory, quantity, uom_id):
+    @classmethod
+    def create_values4complete(cls, product_id, inventory, quantity, uom_id):
         '''
         Return create values to complete inventory
-
-        :param product_id: the product.product id
-        :param inventory: a BrowseRecord of inventory.inventory
-        :param quantity: the actual product quantity for the inventory location
-        :param uom_id: the UoM id of the product_id
-        :return: a dictionary
         '''
         return {
             'inventory': inventory.id,
@@ -362,5 +332,3 @@
             'quantity': max(quantity, 0.0),
             'uom': uom_id,
         }
-
-InventoryLine()
diff -r 091f89c47ad9 trytond/modules/stock/location.py
--- a/trytond/modules/stock/location.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/location.py	Mon Sep 24 20:36:42 2012 +0200
@@ -7,7 +7,11 @@
 from trytond.backend import TableHandler
 from trytond.pyson import Not, Bool, Eval, Equal, PYSONEncoder, Date
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
+
+__all__ = ['Location', 'Party', 'ProductsByLocationsStart',
+    'ProductsByLocations']
+__metaclass__ = PoolMeta
 
 STATES = {
     'readonly': Not(Bool(Eval('active'))),
@@ -17,8 +21,7 @@
 
 class Location(ModelSQL, ModelView):
     "Stock Location"
-    _name = 'stock.location'
-    _description = __doc__
+    __name__ = 'stock.location'
     name = fields.Char("Name", size=None, required=True, states=STATES,
         depends=DEPENDS, translate=True)
     code = fields.Char("Code", size=None, states=STATES, depends=DEPENDS,
@@ -88,109 +91,121 @@
     cost_value = fields.Function(fields.Numeric('Cost Value'),
         'get_cost_value')
 
-    def __init__(self):
-        super(Location, self).__init__()
-        self._order.insert(0, ('name', 'ASC'))
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Location, cls).__setup__()
+        cls._order.insert(0, ('name', 'ASC'))
+        cls._constraints += [
             ('check_recursion', 'recursive_locations'),
             ('check_type_for_moves', 'invalid_type_for_moves'),
-        ]
-        self._error_messages.update({
-            'recursive_locations': 'You can not create recursive locations!',
-            'invalid_type_for_moves': 'A location with existing moves ' \
-                'cannot be changed to a type that does not support moves.',
-            'child_of_warehouse': 'Location "%s" must be a child of ' \
-                'warehouse "%s"!',
-        })
+            ]
+        cls._error_messages.update({
+                'recursive_locations': \
+                    'You can not create recursive locations!',
+                'invalid_type_for_moves': 'A location with existing moves ' \
+                    'cannot be changed to a type that does not support moves.',
+                'child_of_warehouse': 'Location "%s" must be a child of ' \
+                    'warehouse "%s"!',
+                })
 
-    def init(self, module_name):
-        super(Location, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Location, cls).__register__(module_name)
         cursor = Transaction().cursor
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action(['left', 'right'], 'add')
 
-    def check_type_for_moves(self, ids):
+    def check_type_for_moves(self):
         """ Check locations with moves have types compatible with moves. """
         invalid_move_types = ['warehouse', 'view']
-        move_obj = Pool().get('stock.move')
-        for location in self.browse(ids):
-            if location.type in invalid_move_types and \
-                move_obj.search(['OR',
-                    ('to_location', '=', location.id),
-                    ('from_location', '=', location.id),
-                    ]):
-                return False
+        Move = Pool().get('stock.move')
+        if (self.type in invalid_move_types
+                and Move.search(['OR',
+                        ('to_location', '=', self.id),
+                        ('from_location', '=', self.id),
+                        ])):
+            return False
         return True
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_left(self):
+    @staticmethod
+    def default_left():
         return 0
 
-    def default_right(self):
+    @staticmethod
+    def default_right():
         return 0
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'storage'
 
-    def check_xml_record(self, ids, values):
+    @classmethod
+    def check_xml_record(self, records, values):
         return True
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([
-            ('code', '=', clause[2]),
-            ], order=[])
-        if ids:
-            return [('id', 'in', ids)]
-        return [(self._rec_name,) + tuple(clause[1:])]
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        locations = cls.search([
+                ('code', '=', clause[2]),
+                ], order=[])
+        if locations:
+            return [('id', 'in', [l.id for l in locations])]
+        return [(cls._rec_name,) + tuple(clause[1:])]
 
-    def get_quantity(self, ids, name):
-        product_obj = Pool().get('product.product')
-        date_obj = Pool().get('ir.date')
+    @classmethod
+    def get_quantity(cls, locations, name):
+        pool = Pool()
+        Product = pool.get('product.product')
+        Date_ = pool.get('ir.date')
 
         if (not Transaction().context.get('product')) \
                 or not (isinstance(Transaction().context['product'],
                     (int, long))):
-            return dict([(i, 0) for i in ids])
+            return dict([(l.id, 0) for l in locations])
 
         with Transaction().set_context(active_test=False):
-            if not product_obj.search([
-                ('id', '=', Transaction().context['product']),
-                ]):
-                return dict([(i, 0) for i in ids])
+            if not Product.search([
+                        ('id', '=', Transaction().context['product']),
+                        ]):
+                return dict([(l.id, 0) for l in locations])
 
         context = {}
         if (name == 'quantity'
                 and Transaction().context.get('stock_date_end') >
-                date_obj.today()):
-            context['stock_date_end'] = date_obj.today()
+                Date_.today()):
+            context['stock_date_end'] = Date_.today()
 
         if name == 'forecast_quantity':
             context['forecast'] = True
             if not Transaction().context.get('stock_date_end'):
                 context['stock_date_end'] = datetime.date.max
 
+        location_ids = [l.id for l in locations]
         with Transaction().set_context(context):
-            pbl = product_obj.products_by_location(location_ids=ids,
-                    product_ids=[Transaction().context['product']],
-                    with_childs=True, skip_zero=False).iteritems()
+            pbl = Product.products_by_location(location_ids=location_ids,
+                product_ids=[Transaction().context['product']],
+                with_childs=True, skip_zero=False).iteritems()
 
         return dict([(loc, qty) for (loc, prod), qty in pbl])
 
-    def get_cost_value(self, ids, name):
-        product_obj = Pool().get('product.product')
+    @classmethod
+    def get_cost_value(cls, locations, name):
+        Product = Pool().get('product.product')
         trans_context = Transaction().context
         product_id = trans_context.get('product')
         if not product_id:
-            return dict((id, None) for id in ids)
+            return dict((l.id, None) for l in locations)
         cost_values, context = {}, {}
         if 'stock_date_end' in trans_context:
             context['_datetime'] = trans_context['stock_date_end']
         with Transaction().set_context(context):
-            product = product_obj.browse(product_id)
-            for location in self.browse(ids):
+            product = Product(product_id)
+            for location in locations:
                 # The date could be before the product creation
                 if not isinstance(product.cost_price, Decimal):
                     cost_values[location.id] = None
@@ -199,79 +214,68 @@
                         * product.cost_price)
         return cost_values
 
-    def _set_warehouse_parent(self, locations):
+    @classmethod
+    def _set_warehouse_parent(cls, locations):
         '''
         Set the parent of child location of warehouse if not set
-
-        :param locations: a BrowseRecordList of locations
-        :return: a list with updated location ids
         '''
-        location_ids = set()
+        to_update = set()
         for location in locations:
             if location.type == 'warehouse':
                 if not location.input_location.parent:
-                    location_ids.add(location.input_location.id)
+                    to_update.add(location.input_location)
                 if not location.output_location.parent:
-                    location_ids.add(location.output_location.id)
+                    to_update.add(location.output_location)
                 if not location.storage_location.parent:
-                    location_ids.add(location.storage_location.id)
-        location_ids = list(location_ids)
-        if location_ids:
-            self.write(location_ids, {
+                    to_update.add(location.storage_location)
+        if to_update:
+            cls.write(list(to_update), {
                 'parent': location.id,
                 })
 
-    def create(self, vals):
-        res = super(Location, self).create(vals)
-        locations = self.browse([res])
-        self._set_warehouse_parent(locations)
-        return res
+    @classmethod
+    def create(cls, vals):
+        location = super(Location, cls).create(vals)
+        cls._set_warehouse_parent([location])
+        return location
 
-    def write(self, ids, vals):
-        res = super(Location, self).write(ids, vals)
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        locations = self.browse(ids)
-        self._set_warehouse_parent(locations)
+    @classmethod
+    def write(cls, locations, vals):
+        super(Location, cls).write(locations, vals)
+        cls._set_warehouse_parent(locations)
 
-        check_wh = self.search([
-            ('type', '=', 'warehouse'),
-            ['OR',
-                ('storage_location', 'in', ids),
-                ('input_location', 'in', ids),
-                ('output_location', 'in', ids),
-            ]])
+        ids = [l.id for l in locations]
+        warehouses = cls.search([
+                ('type', '=', 'warehouse'),
+                ['OR',
+                    ('storage_location', 'in', ids),
+                    ('input_location', 'in', ids),
+                    ('output_location', 'in', ids),
+                    ]])
 
-        warehouses = self.browse(check_wh)
         fields = ('storage_location', 'input_location', 'output_location')
         wh2childs = {}
         for warehouse in warehouses:
-            in_out_sto = (warehouse[f].id for f in fields)
+            in_out_sto = (getattr(warehouse, f).id for f in fields)
             for location in locations:
                 if location.id not in in_out_sto:
                     continue
-                childs = wh2childs.setdefault(warehouse.id, self.search([
-                    ('parent', 'child_of', warehouse.id),
-                    ]))
-                if location.id not in childs:
-                    self.raise_user_error('child_of_warehouse',
+                childs = wh2childs.setdefault(warehouse.id, cls.search([
+                            ('parent', 'child_of', warehouse.id),
+                            ]))
+                if location not in childs:
+                    cls.raise_user_error('child_of_warehouse',
                         (location.name, warehouse.name))
 
-        return res
-
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, locations, default=None):
         if default is None:
             default = {}
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
 
         default['left'] = 0
         default['right'] = 0
 
         res = []
-        locations = self.browse(ids)
         for location in locations:
             if location.type == 'warehouse':
 
@@ -282,8 +286,8 @@
                 wh_default['storage_location'] = None
                 wh_default['childs'] = None
 
-                new_id = super(Location, self).copy(location.id,
-                        default=wh_default)
+                new_location, = super(Location, cls).copy([location],
+                    default=wh_default)
 
                 with Transaction().set_context(
                         cp_warehouse_locations={
@@ -291,34 +295,31 @@
                             'output_location': location.output_location.id,
                             'storage_location': location.storage_location.id,
                             },
-                        cp_warehouse_id=new_id):
-                    self.copy([c.id for c in location.childs],
-                            default={'parent': new_id})
-                self.write(new_id, {
-                    'type': 'warehouse',
-                    })
+                        cp_warehouse_id=new_location.id):
+                    cls.copy(location.childs,
+                        default={'parent': new_location.id})
+                cls.write([new_location], {
+                        'type': 'warehouse',
+                        })
             else:
-                new_id = super(Location, self).copy(location.id,
-                        default=default)
+                new_location, = super(Location, cls).copy([location],
+                    default=default)
                 warehouse_locations = Transaction().context.get(
-                        'cp_warehouse_locations') or {}
+                    'cp_warehouse_locations') or {}
+                cp_warehouse = cls(Transaction().context['cp_warehouse_id'])
                 if location.id in warehouse_locations.values():
                     for field, loc_id in warehouse_locations.iteritems():
                         if loc_id == location.id:
-                            self.write(
-                                Transaction().context['cp_warehouse_id'], {
-                                    field: new_id,
+                            cls.write([cp_warehouse], {
+                                    field: new_location.id,
                                     })
 
-            res.append(new_id)
+            res.append(new_location)
+        return res
 
-        return int_id and res[0] or res
 
-Location()
-
-
-class Party(ModelSQL, ModelView):
-    _name = 'party.party'
+class Party:
+    __name__ = 'party.party'
     supplier_location = fields.Property(fields.Many2One('stock.location',
         'Supplier Location', domain=[('type', '=', 'supplier')],
         help='The default source location when receiving products from the '
@@ -328,30 +329,25 @@
         help='The default destination location when sending products to the '
         'party.'))
 
-Party()
-
 
 class ProductsByLocationsStart(ModelView):
     'Products by Locations'
-    _name = 'stock.products_by_locations.start'
-    _description = __doc__
+    __name__ = 'stock.products_by_locations.start'
     forecast_date = fields.Date(
         'At Date', help='Allow to compute expected '\
             'stock quantities for this date.\n'\
             '* An empty value is an infinite date in the future.\n'\
             '* A date in the past will provide historical values.')
 
-    def default_forecast_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
-
-ProductsByLocationsStart()
+    @staticmethod
+    def default_forecast_date():
+        Date_ = Pool().get('ir.date')
+        return Date_.today()
 
 
 class ProductsByLocations(Wizard):
     'Products by Locations'
-    _name = 'stock.products_by_locations'
-
+    __name__ = 'stock.products_by_locations'
     start = StateView('stock.products_by_locations.start',
         'stock.products_by_locations_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -359,30 +355,28 @@
             ])
     open = StateAction('stock.act_products_by_locations')
 
-    def do_open(self, session, action):
+    def do_open(self, action):
         pool = Pool()
-        location_obj = pool.get('stock.location')
-        lang_obj = pool.get('ir.lang')
+        Location = pool.get('stock.location')
+        Lang = pool.get('ir.lang')
 
         context = {}
         context['locations'] = Transaction().context.get('active_ids')
-        date = session.start.forecast_date or datetime.date.max
+        date = self.start.forecast_date or datetime.date.max
         context['stock_date_end'] = Date(date.year, date.month, date.day)
         action['pyson_context'] = PYSONEncoder().encode(context)
 
-        locations = location_obj.browse(context['locations'])
+        locations = Location.browse(context['locations'])
 
         for code in [Transaction().language, 'en_US']:
-            lang_ids = lang_obj.search([
+            langs = Lang.search([
                     ('code', '=', code),
                     ])
-            if lang_ids:
+            if langs:
                 break
-        lang = lang_obj.browse(lang_ids[0])
-        date = lang_obj.strftime(date, lang.code, lang.date)
+        lang = langs[0]
+        date = Lang.strftime(date, lang.code, lang.date)
 
         action['name'] += ' - (%s) @ %s' % (
             ','.join(l.name for l in locations), date)
         return action, {}
-
-ProductsByLocations()
diff -r 091f89c47ad9 trytond/modules/stock/move.py
--- a/trytond/modules/stock/move.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/move.py	Mon Sep 24 20:36:42 2012 +0200
@@ -8,6 +8,8 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Move']
+
 STATES = {
     'readonly': In(Eval('state'), ['cancel', 'assigned', 'done']),
 }
@@ -16,8 +18,7 @@
 
 class Move(ModelSQL, ModelView):
     "Stock Move"
-    _name = 'stock.move'
-    _description = __doc__
+    __name__ = 'stock.move'
     _order_name = 'product'
     product = fields.Many2One("product.product", "Product", required=True,
         select=True, states=STATES,
@@ -28,7 +29,7 @@
     product_uom_category = fields.Function(
         fields.Many2One('product.uom.category', 'Product Uom Category',
             on_change_with=['product']),
-        'get_product_uom_category')
+        'on_change_with_product_uom_category')
     uom = fields.Many2One("product.uom", "Uom", required=True, states=STATES,
         domain=[
             ('category', '=', Eval('product_uom_category')),
@@ -37,7 +38,7 @@
             'from_location', 'to_location'],
         depends=['state', 'product_uom_category'])
     unit_digits = fields.Function(fields.Integer('Unit Digits',
-        on_change_with=['uom']), 'get_unit_digits')
+        on_change_with=['uom']), 'on_change_with_unit_digits')
     quantity = fields.Float("Quantity", required=True,
         digits=(16, Eval('unit_digits', 2)), states=STATES,
         depends=['state', 'unit_digits'])
@@ -107,11 +108,12 @@
         depends=['unit_price_required', 'state'])
     unit_price_required = fields.Function(fields.Boolean('Unit Price Required',
         on_change_with=['from_location', 'to_location']),
-        'get_unit_price_required')
+        'on_change_with_unit_price_required')
 
-    def __init__(self):
-        super(Move, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Move, cls).__setup__()
+        cls._sql_constraints += [
             ('check_move_qty_pos',
                 'CHECK(quantity >= 0.0)', 'Move quantity must be positive'),
             ('check_move_internal_qty_pos',
@@ -133,11 +135,11 @@
                         '<= 1)',
                 'Move can be on only one Shipment'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_period_closed', 'period_closed'),
         ]
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
             'set_state_draft': 'You can not set state to draft!',
             'set_state_assigned': 'You can not set state to assigned!',
             'set_state_done': 'You can not set state to done!',
@@ -148,10 +150,11 @@
                 'in the state: "Assigned", "Done" or "Cancel"'),
             })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.drop_constraint('check_packing')
         for suffix in ('in', 'out', 'in_return', 'out_return', 'internal'):
             old_column = 'packing_%s' % suffix
@@ -164,248 +167,190 @@
         # Migration from 1.8: new field internal_quantity
         internal_quantity_exist = table.column_exist('internal_quantity')
 
-        super(Move, self).init(module_name)
+        super(Move, cls).__register__(module_name)
 
         # Migration from 1.8: fill new field internal_quantity
         if not internal_quantity_exist:
             offset = 0
             limit = cursor.IN_MAX
-            move_ids = True
-            while move_ids:
-                move_ids = self.search([], offset=offset, limit=limit)
+            moves = True
+            while moves:
+                moves = cls.search([], offset=offset, limit=limit)
                 offset += limit
-                for move in self.browse(move_ids):
-                    internal_quantity = self._get_internal_quantity(
+                for move in moves:
+                    internal_quantity = cls._get_internal_quantity(
                             move.quantity, move.uom, move.product)
-                    self.write(move.id, {
+                    cls.write([move], {
                         'internal_quantity': internal_quantity,
                         })
-            table = TableHandler(cursor, self, module_name)
+            table = TableHandler(cursor, cls, module_name)
             table.not_null_action('internal_quantity', action='add')
 
         # Migration from 1.0 check_packing_in_out has been removed
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.drop_constraint('check_packing_in_out')
 
         # Add index on create_date
         table.index_action('create_date', action='add')
 
-    def default_planned_date(self):
+    @staticmethod
+    def default_planned_date():
         return Transaction().context.get('planned_date')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def default_currency(self):
-        company_obj = Pool().get('company.company')
+    @staticmethod
+    def default_currency():
+        Company = Pool().get('company.company')
         company = Transaction().context.get('company')
         if company:
-            company = company_obj.browse(company)
+            company = Company(company)
             return company.currency.id
 
-    def default_unit_digits(self):
+    @staticmethod
+    def default_unit_digits():
         return 2
 
-    def on_change_with_unit_digits(self, vals):
-        uom_obj = Pool().get('product.uom')
-        if vals.get('uom'):
-            uom = uom_obj.browse(vals['uom'])
-            return uom.digits
+    def on_change_with_unit_digits(self, name=None):
+        if self.uom:
+            return self.uom.digits
         return 2
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for move in self.browse(ids):
-            res[move.id] = move.uom.digits
-        return res
-
-    def on_change_product(self, vals):
+    def on_change_product(self):
         pool = Pool()
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
-        currency_obj = pool.get('currency.currency')
-        company_obj = pool.get('company.company')
-        location_obj = pool.get('stock.location')
+        Uom = pool.get('product.uom')
+        Currency = pool.get('currency.currency')
 
         res = {
             'unit_price': Decimal('0.0'),
-        }
-        if vals.get('product'):
-            product = product_obj.browse(vals['product'])
-            res['uom'] = product.default_uom.id
-            res['uom.rec_name'] = product.default_uom.rec_name
-            res['unit_digits'] = product.default_uom.digits
-            from_location, to_location = None, None
-            if vals.get('from_location'):
-                from_location = location_obj.browse(vals['from_location'])
-            if vals.get('to_location'):
-                to_location = location_obj.browse(vals['to_location'])
+            }
+        if self.product:
+            res['uom'] = self.product.default_uom.id
+            res['uom.rec_name'] = self.product.default_uom.rec_name
+            res['unit_digits'] = self.product.default_uom.digits
             unit_price = None
-            if from_location and from_location.type in ('supplier',
+            if self.from_location and self.from_location.type in ('supplier',
                     'production'):
-                unit_price = product.cost_price
-            elif to_location and to_location.type == 'customer':
-                unit_price = product.list_price
+                unit_price = self.product.cost_price
+            elif self.to_location and self.to_location.type == 'customer':
+                unit_price = self.product.list_price
             if unit_price:
-                if vals.get('uom') and vals['uom'] != product.default_uom.id:
-                    uom = uom_obj.browse(vals['uom'])
-                    unit_price = uom_obj.compute_price(product.default_uom,
-                            unit_price, uom)
-                if vals.get('currency') and vals.get('company'):
-                    currency = currency_obj.browse(vals['currency'])
-                    company = company_obj.browse(vals['company'])
-                    unit_price = currency_obj.compute(company.currency,
-                            unit_price, currency, round=False)
+                if self.uom != self.product.default_uom:
+                    unit_price = Uom.compute_price(self.product.default_uom,
+                        unit_price, self.uom)
+                if self.currency and self.company:
+                    unit_price = Currency.compute(self.company.currency,
+                        unit_price, self.currency, round=False)
                 res['unit_price'] = unit_price
         return res
 
-    def on_change_with_product_uom_category(self, values):
+    def on_change_with_product_uom_category(self, name=None):
+        if self.product:
+            return self.product.default_uom_category.id
+
+    def on_change_uom(self):
         pool = Pool()
-        product_obj = pool.get('product.product')
-        if values.get('product'):
-            product = product_obj.browse(values['product'])
-            return product.default_uom_category.id
-
-    def get_product_uom_category(self, ids, name):
-        categories = {}
-        for move in self.browse(ids):
-            categories[move.id] = move.product.default_uom_category.id
-        return categories
-
-    def on_change_uom(self, vals):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
-        currency_obj = pool.get('currency.currency')
-        company_obj = pool.get('company.company')
-        location_obj = pool.get('stock.location')
+        Uom = pool.get('product.uom')
+        Currency = pool.get('currency.currency')
 
         res = {
             'unit_price': Decimal('0.0'),
-        }
-        if vals.get('product'):
-            product = product_obj.browse(vals['product'])
-            to_location = None
-            if vals.get('to_location'):
-                to_location = location_obj.browse(vals['to_location'])
-            if to_location and to_location.type == 'storage':
-                unit_price = product.cost_price
-                if vals.get('uom') and vals['uom'] != product.default_uom.id:
-                    uom = uom_obj.browse(vals['uom'])
-                    unit_price = uom_obj.compute_price(product.default_uom,
-                            unit_price, uom)
-                if vals.get('currency') and vals.get('company'):
-                    currency = currency_obj.browse(vals['currency'])
-                    company = company_obj.browse(vals['company'])
-                    unit_price = currency_obj.compute(company.currency,
-                            unit_price, currency, round=False)
+            }
+        if self.product:
+            if self.to_location and self.to_location.type == 'storage':
+                unit_price = self.product.cost_price
+                if self.uom and self.uom != self.product.default_uom:
+                    unit_price = Uom.compute_price(self.product.default_uom,
+                        unit_price, self.uom)
+                if self.currency and self.company:
+                    unit_price = Currency.compute(self.company.currency,
+                        unit_price, self.currency, round=False)
                 res['unit_price'] = unit_price
         return res
 
-    def on_change_with_unit_price_required(self, vals):
-        location_obj = Pool().get('stock.location')
-        if vals.get('from_location'):
-            from_location = location_obj.browse(vals['from_location'])
-            if from_location.type in ('supplier', 'production'):
-                return True
-        if vals.get('to_location'):
-            to_location = location_obj.browse(vals['to_location'])
-            if to_location.type in ('customer', 'production'):
-                return True
+    def on_change_with_unit_price_required(self, name=None):
+        if (self.from_location
+                and self.from_location.type in ('supplier', 'production')):
+            return True
+        if (self.to_location
+                and self.to_location.type == 'customer'):
+            return True
         return False
 
-    def get_unit_price_required(self, ids, name):
-        res = {}
-        for move in self.browse(ids):
-            res[move.id] = False
-            if move.from_location.type in ('supplier', 'production'):
-                res[move.id] = True
-            if move.to_location.type == 'customer':
-                res[move.id] = True
-        return res
-
-    def check_period_closed(self, ids):
-        period_obj = Pool().get('stock.period')
-        period_ids = period_obj.search([
-            ('state', '=', 'closed'),
-        ], order=[('date', 'DESC')], limit=1)
-        if period_ids:
-            period, = period_obj.browse(period_ids)
-            for move in self.browse(ids):
+    @classmethod
+    def check_period_closed(cls, moves):
+        Period = Pool().get('stock.period')
+        periods = Period.search([
+                ('state', '=', 'closed'),
+                ], order=[('date', 'DESC')], limit=1)
+        if periods:
+            period, = periods
+            for move in moves:
                 date = (move.effective_date if move.effective_date
                     else move.planned_date)
                 if date and date < period.date:
                     return False
         return True
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
-        moves = self.browse(ids)
-        for m in moves:
-            res[m.id] = ("%s%s %s"
-                % (m.quantity, m.uom.symbol, m.product.rec_name))
-        return res
+    def get_rec_name(self, name):
+        return ("%s%s %s"
+            % (self.quantity, self.uom.symbol, self.product.rec_name))
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         return [('product',) + clause[1:]]
 
-    def _update_product_cost_price(self, product_id, quantity, uom, unit_price,
+    @classmethod
+    def _update_product_cost_price(cls, product_id, quantity, uom, unit_price,
             currency, company, date):
         """
-        Update the cost price on the given product
-
-        :param product_id: the id of the product
-        :param quantity: the quantity of the product, positive if incoming and
-                negative if outgoing
-        :param uom: the uom id or a BrowseRecord of the uom
-        :param unit_price: the unit price of the product
-        :param currency: the currency of the unit price
-        :param company: the company id ot a BrowseRecord of the company
-        :param date: the date for the currency rate calculation
+        Update the cost price on the given product.
+        The quantity must be positive if incoming and negative if outgoing.
+        The date is for the currency rate calculation.
         """
         pool = Pool()
-        uom_obj = pool.get('product.uom')
-        product_obj = pool.get('product.product')
-        product_template_obj = pool.get('product.template')
-        location_obj = pool.get('stock.location')
-        currency_obj = pool.get('currency.currency')
-        company_obj = pool.get('company.company')
-        date_obj = pool.get('ir.date')
+        Uom = pool.get('product.uom')
+        Product = pool.get('product.product')
+        ProductTemplate = pool.get('product.template')
+        Location = pool.get('stock.location')
+        Currency = pool.get('currency.currency')
+        Company = pool.get('company.company')
+        Date = pool.get('ir.date')
 
         if isinstance(uom, (int, long)):
-            uom = uom_obj.browse(uom)
+            uom = Uom(uom)
         if isinstance(currency, (int, long)):
-            currency = currency_obj.browse(currency)
+            currency = Currency(currency)
         if isinstance(company, (int, long)):
-            company = company_obj.browse(company)
+            company = Company(company)
 
         context = {}
-        context['locations'] = location_obj.search([
-            ('type', '=', 'storage'),
-            ])
-        context['stock_date_end'] = date_obj.today()
+        context['locations'] = Location.search([
+                ('type', '=', 'storage'),
+                ])
+        context['stock_date_end'] = Date.today()
         with Transaction().set_context(context):
-            product = product_obj.browse(product_id)
-        qty = uom_obj.compute_qty(uom, quantity, product.default_uom)
+            product = Product(product_id)
+        qty = Uom.compute_qty(uom, quantity, product.default_uom)
 
         qty = Decimal(str(qty))
-        if hasattr(product_obj, 'cost_price'):
+        if hasattr(Product, 'cost_price'):
             product_qty = product.quantity
         else:
             product_qty = product.template.quantity
         product_qty = Decimal(str(product_qty))
         # convert wrt currency
         with Transaction().set_context(date=date):
-            unit_price = currency_obj.compute(currency.id, unit_price,
-                    company.currency.id, round=False)
+            unit_price = Currency.compute(currency, unit_price,
+                company.currency, round=False)
         # convert wrt to the uom
-        unit_price = uom_obj.compute_price(uom, unit_price,
-                product.default_uom)
+        unit_price = Uom.compute_price(uom, unit_price, product.default_uom)
         if product_qty + qty != Decimal('0.0'):
             new_cost_price = (
                 (product.cost_price * product_qty) + (unit_price * qty)
@@ -413,77 +358,77 @@
         else:
             new_cost_price = product.cost_price
 
-        if hasattr(product_obj, 'cost_price'):
-            digits = product_obj.cost_price.digits
+        if hasattr(Product, 'cost_price'):
+            digits = Product.cost_price.digits
         else:
-            digits = product_template_obj.cost_price.digits
+            digits = ProductTemplate.cost_price.digits
         new_cost_price = new_cost_price.quantize(
             Decimal(str(10.0 ** -digits[1])))
 
         with Transaction().set_user(0, set_context=True):
-            product_obj.write(product.id, {
-                'cost_price': new_cost_price,
-                })
+            Product.write([product], {
+                    'cost_price': new_cost_price,
+                    })
 
-    def _get_internal_quantity(self, quantity, uom, product):
-        uom_obj = Pool().get('product.uom')
-        internal_quantity = uom_obj.compute_qty(uom, quantity,
-                product.default_uom, round=True)
+    @staticmethod
+    def _get_internal_quantity(quantity, uom, product):
+        Uom = Pool().get('product.uom')
+        internal_quantity = Uom.compute_qty(uom, quantity,
+            product.default_uom, round=True)
         return internal_quantity
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         pool = Pool()
-        location_obj = pool.get('stock.location')
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
-        date_obj = pool.get('ir.date')
+        Location = pool.get('stock.location')
+        Product = pool.get('product.product')
+        Uom = pool.get('product.uom')
+        Date = pool.get('ir.date')
 
-        today = date_obj.today()
+        today = Date.today()
         vals = vals.copy()
         effective_date = vals.get('effective_date') or today
 
-        product = product_obj.browse(vals['product'])
+        product = Product(vals['product'])
         if vals.get('state') == 'done':
             vals['effective_date'] = effective_date
-            currency_id = vals.get('currency', self.default_currency())
-            company_id = vals.get('company', self.default_company())
-            from_location = location_obj.browse(vals['from_location'])
-            to_location = location_obj.browse(vals['to_location'])
+            currency_id = vals.get('currency', cls.default_currency())
+            company_id = vals.get('company', cls.default_company())
+            from_location = Location(vals['from_location'])
+            to_location = Location(vals['to_location'])
             if (from_location.type in ('supplier', 'production')
                     and to_location.type == 'storage'
                     and product.cost_price_method == 'average'):
-                self._update_product_cost_price(vals['product'],
+                cls._update_product_cost_price(vals['product'],
                         vals['quantity'], vals['uom'], vals['unit_price'],
                         currency_id, company_id, effective_date)
             if (to_location.type == 'supplier'
                     and from_location.type == 'storage'
                     and product.cost_price_method == 'average'):
-                self._update_product_cost_price(vals['product'],
+                cls._update_product_cost_price(vals['product'],
                         -vals['quantity'], vals['uom'], vals['unit_price'],
                         currency_id, company_id, effective_date)
             if not vals.get('cost_price'):
                 # Re-read product to get the updated cost_price
-                product = product_obj.browse(vals['product'])
+                product = Product(vals['product'])
                 vals['cost_price'] = product.cost_price
 
         elif vals.get('state') == 'assigned':
             vals['effective_date'] = effective_date
 
-        uom = uom_obj.browse(vals['uom'])
-        internal_quantity = self._get_internal_quantity(vals['quantity'],
-                uom, product)
+        uom = Uom(vals['uom'])
+        internal_quantity = cls._get_internal_quantity(vals['quantity'],
+            uom, product)
         vals['internal_quantity'] = internal_quantity
-        return super(Move, self).create(vals)
+        return super(Move, cls).create(vals)
 
-    def write(self, ids, vals):
-        date_obj = Pool().get('ir.date')
+    @classmethod
+    def write(cls, moves, vals):
+        Date = Pool().get('ir.date')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        today = date_obj.today()
+        today = Date.today()
         effective_date = vals.get('effective_date') or today
 
-        moves = self.browse(ids)
         if 'state' in vals:
             for move in moves:
                 if vals['state'] == 'cancel':
@@ -492,41 +437,41 @@
                             and move.to_location.type == 'storage'
                             and move.state != 'cancel'
                             and move.product.cost_price_method == 'average'):
-                        self._update_product_cost_price(move.product.id,
+                        cls._update_product_cost_price(move.product.id,
                                 -move.quantity, move.uom, move.unit_price,
                                 move.currency, move.company, today)
                     if (move.to_location.type == 'supplier'
                             and move.from_location.type == 'storage'
                             and move.state != 'cancel'
                             and move.product.cost_price_method == 'average'):
-                        self._update_product_cost_price(move.product.id,
+                        cls._update_product_cost_price(move.product.id,
                                 move.quantity, move.uom, move.unit_price,
                                 move.currency, move.company, today)
 
                 elif vals['state'] == 'draft':
                     if move.state == 'done':
-                        self.raise_user_error('set_state_draft')
+                        cls.raise_user_error('set_state_draft')
                 elif vals['state'] == 'assigned':
                     if move.state in ('cancel', 'done'):
-                        self.raise_user_error('set_state_assigned')
+                        cls.raise_user_error('set_state_assigned')
                     vals['effective_date'] = effective_date
                 elif vals['state'] == 'done':
                     if move.state in ('cancel'):
-                        self.raise_user_error('set_state_done')
+                        cls.raise_user_error('set_state_done')
                     vals['effective_date'] = effective_date
 
                     if (move.from_location.type in ('supplier', 'production')
                             and move.to_location.type == 'storage'
                             and move.state != 'done'
                             and move.product.cost_price_method == 'average'):
-                        self._update_product_cost_price(move.product.id,
+                        cls._update_product_cost_price(move.product.id,
                                 move.quantity, move.uom, move.unit_price,
                                 move.currency, move.company, effective_date)
                     if (move.to_location.type == 'supplier'
                             and move.from_location.type == 'storage'
                             and move.state != 'done'
                             and move.product.cost_price_method == 'average'):
-                        self._update_product_cost_price(move.product.id,
+                        cls._update_product_cost_price(move.product.id,
                                 -move.quantity, move.uom, move.unit_price,
                                 move.currency, move.company, effective_date)
 
@@ -535,54 +480,49 @@
                 'currency'), False):
             for move in moves:
                 if move.state in ('assigned', 'done', 'cancel'):
-                    self.raise_user_error('modify_assigned_done_cancel')
+                    cls.raise_user_error('modify_assigned_done_cancel')
         if reduce(lambda x, y: x or y in vals,
                 ('planned_date', 'effective_date'), False):
             for move in moves:
                 if move.state in ('done', 'cancel'):
-                    self.raise_user_error('modify_assigned_done_cancel')
+                    cls.raise_user_error('modify_assigned_done_cancel')
 
-        res = super(Move, self).write(ids, vals)
+        super(Move, cls).write(moves, vals)
 
         if vals.get('state', '') == 'done':
-            #Re-read the move because cost_price has been updated
-            for move in self.browse(ids):
+            for move in moves:
                 if not move.cost_price:
-                    self.write(move.id, {
-                        'cost_price': move.product.cost_price,
-                        })
+                    cls.write([move], {
+                            'cost_price': move.product.cost_price,
+                            })
 
-        for move in self.browse(ids):
-            internal_quantity = self._get_internal_quantity(move.quantity,
+        for move in moves:
+            internal_quantity = cls._get_internal_quantity(move.quantity,
                     move.uom, move.product)
             if internal_quantity != move.internal_quantity:
                 # Use super to avoid infinite loop
-                super(Move, self).write(move.id, {
-                    'internal_quantity': internal_quantity,
-                    })
-        return res
+                super(Move, cls).write([move], {
+                        'internal_quantity': internal_quantity,
+                        })
 
-    def delete(self, ids):
-        for move in self.browse(ids):
+    @classmethod
+    def delete(cls, moves):
+        for move in moves:
             if move.state not in  ('draft', 'cancel'):
-                self.raise_user_error('del_draft_cancel')
-        return super(Move, self).delete(ids)
+                cls.raise_user_error('del_draft_cancel')
+        super(Move, cls).delete(moves)
 
-    def pick_product(self, move, location_quantities):
+    def pick_product(self, location_quantities):
         """
         Pick the product across the location. Naive (fast) implementation.
-
-        :param move: a BrowseRecord of stock.move
-        :param location_quantities: a list of tuple (location, available_qty)
-            where location is a BrowseRecord of stock.location.
-        :return: a list of tuple (location, quantity) for quantities
-            that can be picked
+        Return a list of tuple (location, quantity) for quantities that can be
+        picked.
         """
         to_pick = []
-        needed_qty = move.quantity
+        needed_qty = self.quantity
         for location, available_qty in location_quantities.iteritems():
             # Ignore available_qty when too small
-            if available_qty < move.uom.rounding:
+            if available_qty < self.uom.rounding:
                 continue
             if needed_qty <= available_qty:
                 to_pick.append((location, needed_qty))
@@ -591,35 +531,35 @@
                 to_pick.append((location, available_qty))
                 needed_qty -= available_qty
         # Force assignation for consumables:
-        if move.product.consumable:
-            to_pick.append((move.from_location, needed_qty))
+        if self.product.consumable:
+            to_pick.append((self.from_location, needed_qty))
             return to_pick
         return to_pick
 
-    def assign_try(self, moves):
+    @classmethod
+    def assign_try(cls, moves):
         '''
         Try to assign moves.
         It will split the moves to assign as much possible.
-
-        :param moves: a BrowseRecordList of stock.move to assign
-        :return: True if succeed or False if not
+        Return True if succeed or False if not.
         '''
         pool = Pool()
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
-        date_obj = pool.get('ir.date')
-        location_obj = pool.get('stock.location')
+        Product = pool.get('product.product')
+        Uom = pool.get('product.uom')
+        Date = pool.get('ir.date')
+        Location = pool.get('stock.location')
 
-        Transaction().cursor.lock(self._table)
+        Transaction().cursor.lock(cls._table)
 
-        location_ids = location_obj.search([
-            ('parent', 'child_of', [x.from_location.id for x in moves]),
-            ])
+        locations = Location.search([
+                ('parent', 'child_of', [x.from_location.id for x in moves]),
+                ])
         with Transaction().set_context(
-                stock_date_end=date_obj.today(),
+                stock_date_end=Date.today(),
                 stock_assign=True):
-            pbl = product_obj.products_by_location(location_ids=location_ids,
-                    product_ids=[m.product.id for m in moves])
+            pbl = Product.products_by_location(
+                location_ids=[l.id for l in locations],
+                product_ids=[m.product.id for m in moves])
 
         success = True
         for move in moves:
@@ -627,17 +567,17 @@
                 continue
             to_location = move.to_location
             location_qties = {}
-            child_ids = location_obj.search([
-                ('parent', 'child_of', [move.from_location.id]),
-                ])
-            for location in location_obj.browse(child_ids):
+            childs = Location.search([
+                    ('parent', 'child_of', [move.from_location.id]),
+                    ])
+            for location in childs:
                 if (location.id, move.product.id) in pbl:
-                    location_qties[location] = uom_obj.compute_qty(
+                    location_qties[location] = Uom.compute_qty(
                             move.product.default_uom,
                             pbl[(location.id, move.product.id)], move.uom,
                             round=False)
 
-            to_pick = self.pick_product(move, location_qties)
+            to_pick = move.pick_product(location_qties)
 
             picked_qties = 0.0
             for _, qty in to_pick:
@@ -646,7 +586,7 @@
             if picked_qties < move.quantity:
                 success = False
                 first = False
-                self.write(move.id, {
+                cls.write([move], {
                     'quantity': move.quantity - picked_qties,
                     })
             else:
@@ -658,12 +598,12 @@
                     'state': 'assigned',
                     }
                 if first:
-                    self.write(move.id, values)
+                    cls.write([move], values)
                     first = False
                 else:
-                    self.copy(move.id, default=values)
+                    cls.copy([move], default=values)
 
-                qty_default_uom = uom_obj.compute_qty(move.uom, qty,
+                qty_default_uom = Uom.compute_qty(move.uom, qty,
                         move.product.default_uom, round=False)
 
                 pbl[(from_location.id, move.product.id)] = (
@@ -673,5 +613,3 @@
                     pbl.get((to_location.id, move.product.id), 0.0)
                     + qty_default_uom)
         return success
-
-Move()
diff -r 091f89c47ad9 trytond/modules/stock/period.py
--- a/trytond/modules/stock/period.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/period.py	Mon Sep 24 20:36:42 2012 +0200
@@ -6,11 +6,12 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Period', 'Cache']
+
 
 class Period(ModelSQL, ModelView):
     'Stock Period'
-    _name = 'stock.period'
-    _description = __doc__
+    __name__ = 'stock.period'
     _rec_name = 'date'
     date = fields.Date('Date', required=True, states={
         'readonly': Equal(Eval('state'), 'closed'),
@@ -26,15 +27,16 @@
         ('closed', 'Closed'),
         ], 'State', select=True, readonly=True)
 
-    def __init__(self):
-        super(Period, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Period, cls).__setup__()
+        cls._error_messages.update({
             'close_period_future_today': ('You can not close a period '
                 'in the future or today!'),
             'close_period_assigned_move': ('You can not close a period when '
                 'there is still assigned moves!'),
         })
-        self._buttons.update({
+        cls._buttons.update({
                 'draft': {
                     'invisible': Eval('state') == 'draft',
                     },
@@ -43,68 +45,68 @@
                     },
                 })
 
+    @classmethod
     @ModelView.button
-    def draft(self, ids):
-        cache_obj = Pool().get('stock.period.cache')
-        cache_ids = []
-        for i in xrange(0, len(ids), Transaction().cursor.IN_MAX):
-            cache_ids.append(cache_obj.search([
-                ('period', 'in', ids[i:i + Transaction().cursor.IN_MAX]),
-            ], order=[]))
-        cache_obj.delete(list(chain(*cache_ids)))
-        self.write(ids, {
-            'state': 'draft',
-        })
+    def draft(cls, periods):
+        Cache = Pool().get('stock.period.cache')
+        caches = []
+        for i in xrange(0, len(periods), Transaction().cursor.IN_MAX):
+            caches.append(Cache.search([
+                ('period', 'in', [p.id for p in
+                                periods[i:i + Transaction().cursor.IN_MAX]]),
+                        ], order=[]))
+        Cache.delete(list(chain(*caches)))
+        cls.write(periods, {
+                'state': 'draft',
+                })
 
+    @classmethod
     @ModelView.button
-    def close(self, ids):
+    def close(cls, periods):
         pool = Pool()
-        product_obj = pool.get('product.product')
-        location_obj = pool.get('stock.location')
-        cache_obj = pool.get('stock.period.cache')
-        move_obj = pool.get('stock.move')
-        date_obj = pool.get('ir.date')
+        Product = pool.get('product.product')
+        Location = pool.get('stock.location')
+        Cache = pool.get('stock.period.cache')
+        Move = pool.get('stock.move')
+        Date = pool.get('ir.date')
 
-        location_ids = location_obj.search([
-            ('type', 'not in', ['warehouse', 'view']),
-        ], order=[])
-        today = date_obj.today()
-        periods = self.browse(ids)
+        locations = Location.search([
+                ('type', 'not in', ['warehouse', 'view']),
+                ], order=[])
+        today = Date.today()
 
         recent_date = max(period.date for period in periods)
         if recent_date >= today:
-            self.raise_user_error('close_period_future_today')
-        if move_obj.search([
-                ('state', '=', 'assigned'),
-                ['OR', [
-                    ('effective_date', '=', None),
-                    ('planned_date', '<=', recent_date),
-                ],
-                    ('effective_date', '<=', recent_date),
-                ]]):
-            self.raise_user_error('close_period_assigned_move')
+            cls.raise_user_error('close_period_future_today')
+        if Move.search([
+                    ('state', '=', 'assigned'),
+                    ['OR', [
+                            ('effective_date', '=', None),
+                            ('planned_date', '<=', recent_date),
+                            ],
+                        ('effective_date', '<=', recent_date),
+                        ]]):
+            cls.raise_user_error('close_period_assigned_move')
 
         for period in periods:
             with Transaction().set_context(
-                stock_date_end=period.date,
-                stock_date_start=None,
-                stock_assign=False,
-                forecast=False,
-                stock_destinations=None,
-            ):
-                pbl = product_obj.products_by_location(location_ids)
+                    stock_date_end=period.date,
+                    stock_date_start=None,
+                    stock_assign=False,
+                    forecast=False,
+                    stock_destinations=None,
+                    ):
+                pbl = Product.products_by_location([l.id for l in locations])
             for (location_id, product_id), quantity in pbl.iteritems():
-                cache_obj.create({
-                    'period': period.id,
-                    'location': location_id,
-                    'product': product_id,
-                    'internal_quantity': quantity,
+                Cache.create({
+                        'period': period.id,
+                        'location': location_id,
+                        'product': product_id,
+                        'internal_quantity': quantity,
+                        })
+        cls.write(periods, {
+                'state': 'closed',
                 })
-        self.write(ids, {
-            'state': 'closed',
-        })
-
-Period()
 
 
 class Cache(ModelSQL, ModelView):
@@ -113,9 +115,7 @@
 
     It is used to store cached computation of stock quantities.
     '''
-    _name = 'stock.period.cache'
-    _description = __doc__.splitlines()[0]
-
+    __name__ = 'stock.period.cache'
     period = fields.Many2One('stock.period', 'Period', required=True,
         readonly=True, select=True, ondelete='CASCADE')
     location = fields.Many2One('stock.location', 'Location', required=True,
@@ -123,5 +123,3 @@
     product = fields.Many2One('product.product', 'Product', required=True,
         readonly=True, select=True, ondelete='CASCADE')
     internal_quantity = fields.Float('Internal Quantity', readonly=True)
-
-Cache()
diff -r 091f89c47ad9 trytond/modules/stock/product.py
--- a/trytond/modules/stock/product.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/product.py	Mon Sep 24 20:36:42 2012 +0200
@@ -1,80 +1,76 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-import copy
 import datetime
 from decimal import Decimal
-from trytond.model import ModelView, ModelSQL, fields
+from trytond.model import ModelSQL, ModelView, fields
 from trytond.wizard import Wizard, StateView, StateAction, Button
 from trytond.pyson import PYSONEncoder, Eval, Or
 from trytond.transaction import Transaction
 from trytond.tools import safe_eval, reduce_ids
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Template', 'Product',
+    'ProductByLocationStart', 'ProductByLocation',
+    'ProductQuantitiesByWarehouse', 'ProductQuantitiesByWarehouseStart',
+    'OpenProductQuantitiesByWarehouse']
+__metaclass__ = PoolMeta
 
-class Template(ModelSQL, ModelView):
-    _name = "product.template"
 
+class Template:
+    __name__ = "product.template"
     quantity = fields.Function(fields.Float('Quantity'), 'sum_product')
     forecast_quantity = fields.Function(fields.Float('Forecast Quantity'),
             'sum_product')
     cost_value = fields.Function(fields.Numeric('Cost Value'),
         'sum_product')
 
-    def sum_product(self, ids, name):
-        res = {}
+    def sum_product(self, name):
         if name not in ('quantity', 'forecast_quantity', 'cost_value'):
             raise Exception('Bad argument')
+        sum_ = 0. if name != 'cost_value' else Decimal(0)
+        for product in self.products:
+            sum_ += getattr(product, name)
+        return sum_
 
-        for template in self.browse(ids):
-            res[template.id] = 0. if name != 'cost_value' else Decimal(0)
-            for product in template.products:
-                res[template.id] += product[name]
-        return res
-
-    def __init__(self):
-        super(Template, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Template, cls).__setup__()
+        cls._error_messages.update({
                 'change_default_uom': 'You cannot change the default uom for '\
                     'a product which is associated to stock moves.',
             })
-        self.cost_price = copy.copy(self.cost_price)
-        self.cost_price.states = copy.copy(self.cost_price.states)
-        self.cost_price.states['required'] = Or(
-            self.cost_price.states.get('required', True),
+        cls.cost_price.states['required'] = Or(
+            cls.cost_price.states.get('required', True),
             Eval('type').in_(['goods', 'assets']))
-        self.cost_price.depends = copy.copy(self.cost_price.depends)
-        self.cost_price.depends.append('type')
-        self._reset_columns()
+        cls.cost_price.depends.append('type')
 
-    def write(self, ids, vals):
-        move_obj = Pool().get('stock.move')
+    @classmethod
+    def write(cls, templates, vals):
+        Move = Pool().get('stock.move')
         cursor = Transaction().cursor
-        if not  vals.get("default_uom"):
-            return super(Template, self).write(ids, vals)
+        if not vals.get("default_uom"):
+            super(Template, cls).write(templates, vals)
+            return
 
-        for i in range(0, len(ids), cursor.IN_MAX):
-            sub_ids = ids[i:i + cursor.IN_MAX]
-            res = self.search([
+        for i in range(0, len(templates), cursor.IN_MAX):
+            sub_ids = [t.id for t in templates[i:i + cursor.IN_MAX]]
+            templates_to_check = cls.search([
                     ('id', 'in', sub_ids),
                     ('default_uom', '!=', vals['default_uom']),
                     ])
 
-            if res:
-                res = move_obj.search([
-                        ('product.template', 'in', res),
-                        ], limit=1)
-                if res:
-                    self.raise_user_error('change_default_uom')
+            if templates_to_check:
+                if Move.search([
+                            ('product.template', 'in',
+                                [t.id for t in templates_to_check]),
+                            ], limit=1):
+                    cls.raise_user_error('change_default_uom')
 
-        return super(Template, self).write(ids, vals)
+        super(Template, cls).write(templates, vals)
 
 
-Template()
-
-
-class Product(ModelSQL, ModelView):
-    _name = "product.product"
-
+class Product:
+    __name__ = "product.product"
     quantity = fields.Function(fields.Float('Quantity'), 'get_quantity',
             searcher='search_quantity')
     forecast_quantity = fields.Function(fields.Float('Forecast Quantity'),
@@ -82,35 +78,37 @@
     cost_value = fields.Function(fields.Numeric('Cost Value'),
         'get_cost_value')
 
-    def get_quantity(self, ids, name):
-        date_obj = Pool().get('ir.date')
+    @classmethod
+    def get_quantity(cls, products, name):
+        Date = Pool().get('ir.date')
 
+        quantities = dict((p.id, 0.0) for p in products)
         if not Transaction().context.get('locations'):
-            return dict((id, 0.0) for id in ids)
+            return quantities
 
         context = {}
         if (name == 'quantity'
                 and Transaction().context.get('stock_date_end')
                 and Transaction().context.get('stock_date_end') >
-                date_obj.today()):
-            context['stock_date_end'] = date_obj.today()
+                Date.today()):
+            context['stock_date_end'] = Date.today()
 
         if name == 'forecast_quantity':
             context['forecast'] = True
             if not Transaction().context.get('stock_date_end'):
                 context['stock_date_end'] = datetime.date.max
         with Transaction().set_context(context):
-            pbl = self.products_by_location(
-                    location_ids=Transaction().context['locations'],
-                    product_ids=ids, with_childs=True)
+            pbl = cls.products_by_location(
+                location_ids=Transaction().context['locations'],
+                product_ids=[p.id for p in products], with_childs=True)
 
-        res = {}.fromkeys(ids, 0.0)
         for location in Transaction().context['locations']:
-            for product in ids:
-                res[product] += pbl.get((location, product), 0.0)
-        return res
+            for product in products:
+                quantities[product.id] += pbl.get((location, product.id), 0.0)
+        return quantities
 
-    def _search_quantity_eval_domain(self, line, domain):
+    @staticmethod
+    def _search_quantity_eval_domain(line, domain):
         field, operator, operand = domain
         value = line.get(field)
         if value == None:
@@ -121,8 +119,9 @@
             operator = "=="
         return (safe_eval(str(value) + operator + str(operand)))
 
-    def search_quantity(self, name, domain=None):
-        date_obj = Pool().get('ir.date')
+    @classmethod
+    def search_quantity(cls, name, domain=None):
+        Date = Pool().get('ir.date')
 
         if not (Transaction().context.get('locations') and domain):
             return []
@@ -131,8 +130,8 @@
         if (name == 'quantity'
                 and Transaction().context.get('stock_date_end')
                 and Transaction().context.get('stock_date_end') >
-                date_obj.today()):
-            context['stock_date_end'] = date_obj.today()
+                Date.today()):
+            context['stock_date_end'] = Date.today()
 
         if name == 'forecast_quantity':
             context['forecast'] = True
@@ -140,7 +139,7 @@
                 context['stock_date_end'] = datetime.date.max
 
         with Transaction().set_context(context):
-            pbl = self.products_by_location(
+            pbl = cls.products_by_location(
                     location_ids=Transaction().context['locations'],
                     with_childs=True, skip_zero=False).iteritems()
 
@@ -153,17 +152,18 @@
                     })
 
         res = [line['product'] for line in processed_lines
-            if self._search_quantity_eval_domain(line, domain)]
+            if cls._search_quantity_eval_domain(line, domain)]
         return [('id', 'in', res)]
 
-    def get_cost_value(self, ids, name):
+    @classmethod
+    def get_cost_value(cls, products, name):
         cost_values = {}
         context = {}
         trans_context = Transaction().context
         if 'stock_date_end' in context:
             context['_datetime'] = trans_context['stock_date_end']
         with Transaction().set_context(context):
-            for product in self.browse(ids):
+            for product in products:
                 # The date could be before the product creation
                 if not isinstance(product.cost_price, Decimal):
                     cost_values[product.id] = None
@@ -172,7 +172,8 @@
                         * product.cost_price)
         return cost_values
 
-    def products_by_location(self, location_ids, product_ids=None,
+    @classmethod
+    def products_by_location(cls, location_ids, product_ids=None,
             with_childs=False, skip_zero=True):
         """
         Compute for each location and product the stock quantity in the default
@@ -190,23 +191,21 @@
                 stock_skip_warehouse: if set, quantities on a warehouse are no
                     more quantities of all child locations but quantities of
                     the storage zone.
+        If product_ids is None all products are used.
+        If with_childs, it computes also for child locations.
+        If skip_zero it lists also items with zero quantity
 
-        :param location_ids: the ids of locations
-        :param product_ids: the ids of the products
-                if None all products are used
-        :param with_childs: a boolean to compute also for child locations
-        :param skip_zero: a boolean to list also items with zero quantity
-        :return: a dictionary with (location id, product id) as key
-                and quantity as value
+        Return a dictionary with (location id, product id) as key
+                and quantity as value.
         """
         pool = Pool()
-        uom_obj = pool.get('product.uom')
-        rule_obj = pool.get('ir.rule')
-        location_obj = pool.get('stock.location')
-        date_obj = pool.get('ir.date')
-        period_obj = pool.get('stock.period')
+        Uom = pool.get('product.uom')
+        Rule = pool.get('ir.rule')
+        Location = pool.get('stock.location')
+        Date = pool.get('ir.date')
+        Period = pool.get('stock.period')
 
-        today = date_obj.today()
+        today = Date.today()
 
         if not location_ids:
             return {}
@@ -218,7 +217,7 @@
         location_ids = set(location_ids)
         storage_to_remove = set()
         wh_to_add = {}
-        for location in location_obj.browse(list(location_ids)):
+        for location in Location.browse(list(location_ids)):
             if (location.type == 'warehouse'
                     and Transaction().context.get('stock_skip_warehouse')):
                 location_ids.remove(location.id)
@@ -228,7 +227,7 @@
                 wh_to_add[location.id] = location.storage_location.id
         location_ids = list(location_ids)
 
-        move_rule_query, move_rule_val = rule_obj.domain_get('stock.move')
+        move_rule_query, move_rule_val = Rule.domain_get('stock.move')
 
         period_clause, period_vals = 'period = %s', [0]
 
@@ -385,21 +384,20 @@
                     context['stock_date_start'], today,
                     ])
         else:
-            period_ids = period_obj.search([
-                ('date', '<', context['stock_date_end']),
-            ], order=[('date', 'DESC')], limit=1)
-            if period_ids:
-                period_id, = period_ids
-                period = period_obj.browse(period_id)
+            periods = Period.search([
+                    ('date', '<', context['stock_date_end']),
+                    ], order=[('date', 'DESC')], limit=1)
+            if periods:
+                period, = periods
                 state_date_clause += (' AND '
                     '(COALESCE(effective_date, planned_date) > %s)')
                 state_date_vals.append(period.date)
                 period_vals[0] = period.id
 
         if with_childs:
-            query, args = location_obj.search([
-                ('parent', 'child_of', location_ids),
-                ], query_string=True, order=[])
+            query, args = Location.search([
+                    ('parent', 'child_of', location_ids),
+                    ], query_string=True, order=[])
             where_clause = " IN (" + query + ") "
             where_vals = args
         else:
@@ -494,13 +492,12 @@
         # Propagate quantities on from child locations to their parents
         if with_childs:
             # Fetch all child locations
-            all_location_ids = location_obj.search([
-                ('parent', 'child_of', location_ids),
-                ])
-            locations = location_obj.browse(all_location_ids)
+            locations = Location.search([
+                    ('parent', 'child_of', location_ids),
+                    ])
             # Generate a set of locations without childs and a dict
             # giving the parent of each location.
-            leafs = set(all_location_ids)
+            leafs = set([l.id for l in locations])
             parent = {}
             for location in locations:
                 if not location.parent:
@@ -527,11 +524,11 @@
 
         # Round quantities
         default_uom = dict((p.id, p.default_uom) for p in
-            self.browse(list(res_product_ids)))
+            cls.browse(list(res_product_ids)))
         for key, quantity in res.iteritems():
             location, product = key
             uom = default_uom[product]
-            res[key] = uom_obj.round(quantity, uom.rounding)
+            res[key] = Uom.round(quantity, uom.rounding)
 
         # Complete result with missing products if asked
         if not skip_zero:
@@ -539,7 +536,7 @@
             if product_ids:
                 all_product_ids = product_ids
             else:
-                all_product_ids = Pool().get("product.product").search([])
+                all_product_ids = cls.search([])
             keys = ((l, p) for l in location_ids for p in all_product_ids)
             for location_id, product_id in keys:
                 if (location_id, product_id) not in res:
@@ -555,30 +552,25 @@
 
         return res
 
-Product()
-
 
 class ProductByLocationStart(ModelView):
     'Product by Location'
-    _name = 'product.by_location.start'
-    _description = __doc__
+    __name__ = 'product.by_location.start'
     forecast_date = fields.Date(
         'At Date', help='Allow to compute expected '\
             'stock quantities for this date.\n'\
             '* An empty value is an infinite date in the future.\n'\
             '* A date in the past will provide historical values.')
 
-    def default_forecast_date(self):
-        date_obj = Pool().get('ir.date')
-        return date_obj.today()
-
-ProductByLocationStart()
+    @staticmethod
+    def default_forecast_date():
+        Date = Pool().get('ir.date')
+        return Date.today()
 
 
 class ProductByLocation(Wizard):
     'Product by Location'
-    _name = 'product.by_location'
-
+    __name__ = 'product.by_location'
     start = StateView('product.by_location.start',
         'stock.product_by_location_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -586,56 +578,56 @@
             ])
     open = StateAction('stock.act_location_quantity_tree')
 
-    def do_open(self, session, action):
-        product_obj = Pool().get('product.product')
-        lang_obj = Pool().get('ir.lang')
+    def do_open(self, action):
+        pool = Pool()
+        Product = pool.get('product.product')
+        Lang = pool.get('ir.lang')
 
         context = {}
         product_id = Transaction().context['active_id']
         context['product'] = product_id
-        if session.start.forecast_date:
-            context['stock_date_end'] = session.start.forecast_date
+        if self.start.forecast_date:
+            context['stock_date_end'] = self.start.forecast_date
         else:
             context['stock_date_end'] = datetime.date.max
         action['pyson_context'] = PYSONEncoder().encode(context)
-        product = product_obj.browse(product_id)
+        product = Product(product_id)
 
         for code in [Transaction().language, 'en_US']:
-            lang_ids = lang_obj.search([
+            langs = Lang.search([
                     ('code', '=', code),
                     ])
-            if lang_ids:
+            if langs:
                 break
-        lang = lang_obj.browse(lang_ids[0])
-        date = lang_obj.strftime(context['stock_date_end'],
+        lang, = langs
+        date = Lang.strftime(context['stock_date_end'],
             lang.code, lang.date)
 
         action['name'] += ' - %s (%s) @ %s' % (product.rec_name,
             product.default_uom.rec_name, date)
         return action, {}
 
-ProductByLocation()
-
 
 class ProductQuantitiesByWarehouse(ModelSQL, ModelView):
     'Product Quantities By Warehouse'
-    _name = 'stock.product_quantities_warehouse'
-    _description = __doc__
-
+    __name__ = 'stock.product_quantities_warehouse'
     date = fields.Date('Date')
     quantity = fields.Function(fields.Float('Quantity'), 'get_quantity')
 
-    def __init__(self):
-        super(ProductQuantitiesByWarehouse, self).__init__()
-        self._order.insert(0, ('date', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ProductQuantitiesByWarehouse, cls).__setup__()
+        cls._order.insert(0, ('date', 'ASC'))
 
-    def table_query(self):
-        move_obj = Pool().get('stock.move')
-        location_obj = Pool().get('stock.location')
+    @staticmethod
+    def table_query():
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Location = pool.get('stock.location')
 
         product_id = Transaction().context.get('product')
         warehouse_id = Transaction().context.get('warehouse', -1)
-        warehouse_clause, warehouse_params = location_obj.search([
+        warehouse_clause, warehouse_params = Location.search([
                 ('parent', 'child_of', [warehouse_id]),
                 ], query_string=True, order=[])
         return ('SELECT MAX(id) AS id, '
@@ -644,20 +636,20 @@
                 'NULL AS write_uid, '
                 'NULL AS write_date, '
                 'COALESCE(effective_date, planned_date) AS date '
-            'FROM "' + move_obj._table + '" '
+            'FROM "' + Move._table + '" '
             'WHERE product = %s '
                 'AND (from_location IN (' + warehouse_clause + ') '
                     'OR to_location IN (' + warehouse_clause + ')) '
                 'AND COALESCE(effective_date, planned_date) IS NOT NULL '
             'GROUP BY date, product', [product_id] + 2 * warehouse_params)
 
-    def get_quantity(self, ids, name):
-        product_obj = Pool().get('product.product')
+    @classmethod
+    def get_quantity(cls, lines, name):
+        Product = Pool().get('product.product')
 
         product_id = Transaction().context.get('product')
         warehouse_id = Transaction().context.get('warehouse')
 
-        lines = self.browse(ids)
         dates = sorted(l.date for l in lines)
         quantities = {}
         date_start = None
@@ -667,7 +659,7 @@
                 'stock_date_end': date,
                 }
             with Transaction().set_context(**context):
-                quantities[date] = product_obj.products_by_location(
+                quantities[date] = Product.products_by_location(
                     [warehouse_id], [product_id], with_childs=True,
                     skip_zero=False)[(warehouse_id, product_id)]
             date_start = date + datetime.timedelta(1)
@@ -678,33 +670,27 @@
 
         return dict((l.id, quantities[l.date]) for l in lines)
 
-ProductQuantitiesByWarehouse()
-
 
 class ProductQuantitiesByWarehouseStart(ModelView):
     'Product Quantities By Warehouse'
-    _name = 'stock.product_quantities_warehouse.start'
-    _description = __doc__
-
+    __name__ = 'stock.product_quantities_warehouse.start'
     warehouse = fields.Many2One('stock.location', 'Warehouse', domain=[
             ('type', '=', 'warehouse'),
             ])
 
-    def default_warehouse(self):
-        location_obj = Pool().get('stock.location')
-        warehouse_ids = location_obj.search([
+    @staticmethod
+    def default_warehouse():
+        Location = Pool().get('stock.location')
+        warehouses = Location.search([
                 ('type', '=', 'warehouse'),
                 ])
-        if len(warehouse_ids) == 1:
-            return warehouse_ids[0]
-
-ProductQuantitiesByWarehouseStart()
+        if len(warehouses) == 1:
+            return warehouses[0].id
 
 
 class OpenProductQuantitiesByWarehouse(Wizard):
     'Product Quantities By Warehouse'
-    _name = 'stock.product_quantities_warehouse'
-
+    __name__ = 'stock.product_quantities_warehouse'
     start = StateView('stock.product_quantities_warehouse.start',
         'stock.product_quantities_warehouse_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -712,15 +698,13 @@
             ])
     open_ = StateAction('stock.act_product_quantities_warehouse')
 
-    def do_open_(self, session, action):
-        date_obj = Pool().get('ir.date')
+    def do_open_(self, action):
+        Date = Pool().get('ir.date')
         action['pyson_context'] = PYSONEncoder().encode({
                 'product': Transaction().context['active_id'],
-                'warehouse': session.start.warehouse.id,
+                'warehouse': self.start.warehouse.id,
                 })
         action['pyson_search_value'] = PYSONEncoder().encode([
-                ('date', '>=', date_obj.today()),
+                ('date', '>=', Date.today()),
                 ])
         return action, {}
-
-OpenProductQuantitiesByWarehouse()
diff -r 091f89c47ad9 trytond/modules/stock/shipment.py
--- a/trytond/modules/stock/shipment.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/shipment.py	Mon Sep 24 20:36:42 2012 +0200
@@ -9,9 +9,22 @@
 from trytond.backend import TableHandler
 from trytond.pyson import Eval, Not, Equal, If, Or, And, Bool, In, Get, Id
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.tools import reduce_ids
 
+__all__ = ['ShipmentIn', 'ShipmentInReturn',
+    'ShipmentOut', 'ShipmentOutReturn',
+    'AssignShipmentOutAssignFailed', 'AssignShipmentOut',
+    'ShipmentInternal',
+    'Address',
+    'AssignShipmentInternalAssignFailed', 'AssignShipmentInternal',
+    'AssignShipmentInReturnAssignFailed', 'AssignShipmentInReturn',
+    'CreateShipmentOutReturn',
+    'DeliveryNote', 'PickingList',
+    'SupplierRestockingList', 'CustomerReturnRestockingList',
+    'InteralShipmentReport']
+__metaclass__ = PoolMeta
+
 STATES = {
     'readonly': "state in ('cancel', 'done')",
 }
@@ -19,10 +32,8 @@
 
 class ShipmentIn(Workflow, ModelSQL, ModelView):
     "Supplier Shipment"
-    _name = 'stock.shipment.in'
-    _description = __doc__
+    __name__ = 'stock.shipment.in'
     _rec_name = 'code'
-
     effective_date = fields.Date('Effective Date', readonly=True)
     planned_date = fields.Date('Planned Date', states={
             'readonly': Not(Equal(Eval('state'), 'draft')),
@@ -48,7 +59,7 @@
         depends=['state', 'incoming_moves', 'supplier'])
     supplier_location = fields.Function(fields.Many2One('stock.location',
             'Supplier Location', on_change_with=['supplier']),
-        'get_supplier_location')
+        'on_change_with_supplier_location')
     contact_address = fields.Many2One('party.address', 'Contact Address',
         states={
             'readonly': Not(Equal(Eval('state'), 'draft')),
@@ -62,10 +73,10 @@
             }, depends=['state', 'incoming_moves'])
     warehouse_input = fields.Function(fields.Many2One('stock.location',
             'Warehouse Input', on_change_with=['warehouse']),
-        'get_warehouse_input')
+        'on_change_with_warehouse_input')
     warehouse_storage = fields.Function(fields.Many2One('stock.location',
             'Warehouse Storage', on_change_with=['warehouse']),
-        'get_warehouse_storage')
+        'on_change_with_warehouse_storage')
     incoming_moves = fields.Function(fields.One2Many('stock.move', None,
             'Incoming Moves',
             add_remove=[
@@ -111,10 +122,11 @@
         ('received', 'Received'),
         ], 'State', readonly=True)
 
-    def __init__(self):
-        super(ShipmentIn, self).__init__()
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentIn, cls).__setup__()
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
                 'incoming_move_input_dest': 'Incoming Moves must have ' \
                     'the warehouse input location as destination location!',
                 'inventory_move_input_source': 'Inventory Moves must ' \
@@ -122,14 +134,14 @@
                 'delete_cancel': 'Supplier Shipment "%s" must be cancelled '\
                     'before deletion!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('draft', 'received'),
                 ('received', 'done'),
                 ('draft', 'cancel'),
                 ('received', 'cancel'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': Eval('state').in_(['cancel', 'done']),
                     },
@@ -144,7 +156,8 @@
                     },
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         cursor.execute("UPDATE ir_model_data "\
@@ -164,8 +177,8 @@
 
         old_table = 'stock_packing_in'
         if TableHandler.table_exist(cursor, old_table):
-            TableHandler.table_rename(cursor, old_table, self._table)
-        table = TableHandler(cursor, self, module_name)
+            TableHandler.table_rename(cursor, old_table, cls._table)
+        table = TableHandler(cursor, cls, module_name)
         for field in ('create_uid', 'write_uid', 'contact_address',
                 'warehouse', 'supplier'):
             table.drop_fk(field, table=old_table)
@@ -175,190 +188,163 @@
         # Migration from 2.0:
         created_company = table.column_exist('company')
 
-        super(ShipmentIn, self).init(module_name)
+        super(ShipmentIn, cls).__register__(module_name)
 
         # Migration from 2.0:
-        move_obj = Pool().get('stock.move')
+        Move = Pool().get('stock.move')
         if (not created_company
-                and TableHandler.table_exist(cursor, move_obj._table)):
+                and TableHandler.table_exist(cursor, Move._table)):
             cursor.execute('SELECT shipment.id, MAX(move.company) '
                 'FROM "%s" AS shipment '
                 'INNER JOIN "%s" AS move ON shipment.id = move.shipment_in '
                 'GROUP BY shipment.id '
                 'ORDER BY MAX(move.company)'
-                % (self._table, move_obj._table))
+                % (cls._table, Move._table))
             for company_id, values in itertools.groupby(cursor.fetchall(),
                     operator.itemgetter(1)):
                 shipment_ids = [x[0] for x in values]
                 for i in range(0, len(shipment_ids), cursor.IN_MAX):
                     sub_ids = shipment_ids[i:i + cursor.IN_MAX]
                     red_sql, red_ids = reduce_ids('id', sub_ids)
-                    cursor.execute('UPDATE "' + self._table + '" '
+                    cursor.execute('UPDATE "' + cls._table + '" '
                         'SET company = %s WHERE ' + red_sql,
                         [company_id] + red_ids)
             table.not_null_action('company', action='add')
 
         # Add index on create_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action('create_date', action='add')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_warehouse(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(self.warehouse.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_warehouse(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.warehouse.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def on_change_supplier(self, values):
-        if not values.get('supplier'):
+    def on_change_supplier(self):
+        if not self.supplier:
             return {'contact_address': None}
-        party_obj = Pool().get("party.party")
-        address_id = party_obj.address_get(values['supplier'])
-        return {'contact_address': address_id}
+        address = self.supplier.address_get()
+        return {'contact_address': address.id}
 
-    def on_change_with_supplier_location(self, values):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        if values.get('supplier'):
-            supplier = party_obj.browse(values['supplier'])
-            return supplier.supplier_location.id
+    def on_change_with_supplier_location(self, name=None):
+        if self.supplier:
+            return self.supplier.supplier_location.id
 
-    def get_supplier_location(self, ids, name):
-        locations = {}
-        for shipment in self.browse(ids):
-            locations[shipment.id] = shipment.supplier.supplier_location.id
-        return locations
+    @classmethod
+    def default_warehouse_input(cls):
+        warehouse = cls.default_warehouse()
+        if warehouse:
+            return cls(warehouse=warehouse).on_change_with_warehouse_input()
 
-    def default_warehouse_input(self):
-        warehouse = self.default_warehouse()
+    def on_change_with_warehouse_input(self, name=None):
+        if self.warehouse:
+            return self.warehouse.input_location.id
+
+    @classmethod
+    def default_warehouse_storage(cls):
+        warehouse = cls.default_warehouse()
         if warehouse:
-            value = self.on_change_with_warehouse_input({
-                    'warehouse': warehouse,
-                    })
-            return value
+            return cls(warehouse=warehouse).on_change_with_warehouse_storage()
 
-    def on_change_with_warehouse_input(self, values):
-        pool = Pool()
-        location_obj = pool.get('stock.location')
-        if values.get('warehouse'):
-            warehouse = location_obj.browse(values['warehouse'])
-            return warehouse.input_location.id
+    def on_change_with_warehouse_storage(self, name=None):
+        if self.warehouse:
+            return self.warehouse.storage_location.id
 
-    def get_warehouse_input(self, ids, name):
-        inputs = {}
-        for shipment in self.browse(ids):
-            inputs[shipment.id] = shipment.warehouse.input_location.id
-        return inputs
+    def get_incoming_moves(self, name):
+        moves = []
+        for move in self.moves:
+            if move.to_location.id == self.warehouse.input_location.id:
+                moves.append(move.id)
+        return moves
 
-    def default_warehouse_storage(self):
-        warehouse = self.default_warehouse()
-        if warehouse:
-            value = self.on_change_with_warehouse_storage({
-                    'warehouse': warehouse,
-                    })
-            return value
-
-    def on_change_with_warehouse_storage(self, values):
-        pool = Pool()
-        location_obj = pool.get('stock.location')
-        if values.get('warehouse'):
-            warehouse = location_obj.browse(values['warehouse'])
-            return warehouse.storage_location.id
-
-    def get_warehouse_storage(self, ids, name):
-        storages = {}
-        for shipment in self.browse(ids):
-            storages[shipment.id] = shipment.warehouse.storage_location.id
-        return storages
-
-    def get_incoming_moves(self, ids, name):
-        res = {}
-        for shipment in self.browse(ids):
-            res[shipment.id] = []
-            for move in shipment.moves:
-                if move.to_location.id == shipment.warehouse.input_location.id:
-                    res[shipment.id].append(move.id)
-        return res
-
-    def set_incoming_moves(self, ids, name, value):
+    @classmethod
+    def set_incoming_moves(cls, shipments, name, value):
         if not value:
             return
-        self.write(ids, {
-            'moves': value,
-            })
+        cls.write(shipments, {
+                'moves': value,
+                })
 
-    def get_inventory_moves(self, ids, name):
-        res = {}
-        for shipment in self.browse(ids):
-            res[shipment.id] = []
-            for move in shipment.moves:
-                if (move.from_location.id ==
-                        shipment.warehouse.input_location.id):
-                    res[shipment.id].append(move.id)
-        return res
+    def get_inventory_moves(self, name):
+        moves = []
+        for move in self.moves:
+            if (move.from_location.id ==
+                    self.warehouse.input_location.id):
+                moves.append(move.id)
+        return moves
 
-    def set_inventory_moves(self, ids, name, value):
+    @classmethod
+    def set_inventory_moves(cls, shipments, name, value):
         if not value:
             return
-        self.write(ids, {
-            'moves': value,
-            })
+        cls.write(shipments, {
+                'moves': value,
+                })
 
-    def _get_move_planned_date(self, shipment):
+    @property
+    def _move_planned_date(self):
         '''
         Return the planned date for incoming moves and inventory_moves
         '''
-        return shipment.planned_date, shipment.planned_date
+        return self.planned_date, self.planned_date
 
-    def _set_move_planned_date(self, shipment_ids):
+    @classmethod
+    def _set_move_planned_date(cls, shipments):
         '''
         Set planned date of moves for the shipments
         '''
-        move_obj = Pool().get('stock.move')
-        if isinstance(shipment_ids, (int, long)):
-            shipment_ids = [shipment_ids]
-        for shipment in self.browse(shipment_ids):
-            dates = self._get_move_planned_date(shipment)
+        Move = Pool().get('stock.move')
+        for shipment in shipments:
+            dates = shipment._move_planned_date
             incoming_date, inventory_date = dates
-            move_obj.write([x.id for x in shipment.incoming_moves
-                    if x.state not in ('assigned', 'done', 'cancel')], {
+            Move.write([m for m in shipment.incoming_moves
+                    if m.state not in ('assigned', 'done', 'cancel')], {
                     'planned_date': incoming_date,
                     })
-            move_obj.write([x.id for x in shipment.inventory_moves
-                    if x.state not in ('assigned', 'done', 'cancel')], {
+            Move.write([m for m in shipment.inventory_moves
+                    if m.state not in ('assigned', 'done', 'cancel')], {
                     'planned_date': inventory_date,
                     })
 
-    def create(self, values):
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('stock.configuration')
+    @classmethod
+    def create(cls, values):
+        pool = Pool()
+        Sequence = pool.get('ir.sequence')
+        Config = pool.get('stock.configuration')
 
         values = values.copy()
-        config = config_obj.browse(1)
-        values['code'] = sequence_obj.get_id(config.shipment_in_sequence.id)
-        shipment_id = super(ShipmentIn, self).create(values)
-        self._set_move_planned_date(shipment_id)
-        return shipment_id
+        config = Config(1)
+        values['code'] = Sequence.get_id(config.shipment_in_sequence)
+        shipment = super(ShipmentIn, cls).create(values)
+        cls._set_move_planned_date([shipment])
+        return shipment
 
-    def write(self, ids, values):
-        result = super(ShipmentIn, self).write(ids, values)
-        self._set_move_planned_date(ids)
-        return result
+    @classmethod
+    def write(cls, shipments, values):
+        super(ShipmentIn, cls).write(shipments, values)
+        cls._set_move_planned_date(shipments)
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, shipments, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default['inventory_moves'] = None
         default['incoming_moves'] = None
-        return super(ShipmentIn, self).copy(ids, default=default)
+        return super(ShipmentIn, cls).copy(shipments, default=default)
 
-    def _get_inventory_moves(self, incoming_move):
+    @classmethod
+    def _get_inventory_moves(cls, incoming_move):
         res = {}
         if incoming_move.quantity <= 0.0:
             return None
@@ -375,85 +361,82 @@
         res['company'] = incoming_move.company.id
         return res
 
-    def create_inventory_moves(self, shipments):
+    @classmethod
+    def create_inventory_moves(cls, shipments):
         for shipment in shipments:
             inventory_moves = []
             for incoming_move in shipment.incoming_moves:
-                vals = self._get_inventory_moves(incoming_move)
+                vals = cls._get_inventory_moves(incoming_move)
                 if vals:
                     inventory_moves.append(('create', vals))
             if inventory_moves:
-                self.write(shipment.id, {
+                cls.write([shipment], {
                     'inventory_moves': inventory_moves,
                     })
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, shipments):
         # Cancel before delete
-        self.cancel(ids)
-        for shipment in self.browse(ids):
+        cls.cancel(shipments)
+        for shipment in shipments:
             if shipment.state != 'cancel':
-                self.raise_user_error('delete_cancel', shipment.rec_name)
-        return super(ShipmentIn, self).delete(ids)
+                cls.raise_user_error('delete_cancel', shipment.rec_name)
+        super(ShipmentIn, cls).delete(shipments)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments
+    def cancel(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments
                 for m in s.incoming_moves + s.inventory_moves
                 if m.state not in ('cancel', 'done')], {
                 'state': 'cancel',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.incoming_moves
+    def draft(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.incoming_moves
                 if m.state not in ('draft', 'done')], {
                 'state': 'draft',
                 })
-        move_obj.delete([m.id for s in shipments for m in s.inventory_moves
+        Move.delete([m for s in shipments for m in s.inventory_moves
                 if m.state in ('draft', 'cancel')])
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('received')
-    def receive(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.incoming_moves
+    def receive(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.incoming_moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
-        self.create_inventory_moves(shipments)
+        cls.create_inventory_moves(shipments)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('done')
-    def done(self, ids):
-        move_obj = Pool().get('stock.move')
-        date_obj = Pool().get('ir.date')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.inventory_moves
+    def done(cls, shipments):
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Date = pool.get('ir.date')
+        Move.write([m for s in shipments for m in s.inventory_moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
-        self.write(ids, {
-                'effective_date': date_obj.today(),
+        cls.write(shipments, {
+                'effective_date': Date.today(),
                 })
 
-ShipmentIn()
-
 
 class ShipmentInReturn(Workflow, ModelSQL, ModelView):
     "Supplier Return Shipment"
-    _name = 'stock.shipment.in.return'
-    _description = __doc__
+    __name__ = 'stock.shipment.in.return'
     _rec_name = 'code'
-
     effective_date = fields.Date('Effective Date', readonly=True)
     planned_date = fields.Date('Planned Date',
         states={
@@ -505,14 +488,15 @@
         ('done', 'Done'),
         ], 'State', readonly=True)
 
-    def __init__(self):
-        super(ShipmentInReturn, self).__init__()
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentInReturn, cls).__setup__()
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
                 'delete_cancel': 'Supplier Return Shipment "%s" must be '\
                     'cancelled before deletion!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('draft', 'waiting'),
                 ('waiting', 'assigned'),
                 ('waiting', 'draft'),
@@ -523,7 +507,7 @@
                 ('assigned', 'cancel'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': Eval('state').in_(['cancel', 'done']),
                     },
@@ -549,13 +533,14 @@
                 'assign_force': {},
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         old_table = 'stock_packing_in_return'
         if TableHandler.table_exist(cursor, old_table):
-            TableHandler.table_rename(cursor, old_table, self._table)
-        table = TableHandler(cursor, self, module_name)
+            TableHandler.table_rename(cursor, old_table, cls._table)
+        table = TableHandler(cursor, cls, module_name)
         for field in ('create_uid', 'write_uid', 'from_location',
                 'to_location'):
             table.drop_fk(field, table=old_table)
@@ -565,173 +550,177 @@
         # Migration from 2.0:
         created_company = table.column_exist('company')
 
-        super(ShipmentInReturn, self).init(module_name)
+        super(ShipmentInReturn, cls).__register__(module_name)
 
         # Migration from 2.0:
-        move_obj = Pool().get('stock.move')
+        Move = Pool().get('stock.move')
         if (not created_company
-                and TableHandler.table_exist(cursor, move_obj._table)):
+                and TableHandler.table_exist(cursor, Move._table)):
             cursor.execute('SELECT shipment.id, MAX(move.company) '
                 'FROM "%s" AS shipment '
                 'INNER JOIN "%s" AS move '
                 'ON shipment.id = move.shipment_in_return '
                 'GROUP BY shipment.id '
                 'ORDER BY MAX(move.company)'
-                % (self._table, move_obj._table))
+                % (cls._table, Move._table))
             for company_id, values in itertools.groupby(cursor.fetchall(),
                     operator.itemgetter(1)):
                 shipment_ids = [x[0] for x in values]
                 for i in range(0, len(shipment_ids), cursor.IN_MAX):
                     sub_ids = shipment_ids[i:i + cursor.IN_MAX]
                     red_sql, red_ids = reduce_ids('id', sub_ids)
-                    cursor.execute('UPDATE "' + self._table + '" '
+                    cursor.execute('UPDATE "' + cls._table + '" '
                         'SET company = %s WHERE ' + red_sql,
                         [company_id] + red_ids)
             table.not_null_action('company', action='add')
 
         # Add index on create_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action('create_date', action='add')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def _get_move_planned_date(self, shipment):
+    @property
+    def _move_planned_date(self):
         '''
         Return the planned date for the moves
         '''
-        return shipment.planned_date
+        return self.planned_date
 
-    def _set_move_planned_date(self, shipment_ids):
+    @classmethod
+    def _set_move_planned_date(cls, shipments):
         '''
         Set planned date of moves for the shipments
         '''
-        move_obj = Pool().get('stock.move')
-        if isinstance(shipment_ids, (int, long)):
-            shipment_ids = [shipment_ids]
-        for shipment in self.browse(shipment_ids):
-            date = self._get_move_planned_date(shipment)
-            move_obj.write([x.id for x in shipment.moves
-                    if x.state not in ('assigned', 'done', 'cancel')], {
-                    'planned_date': date,
+        Move = Pool().get('stock.move')
+        for shipment in shipments:
+            Move.write([m for m in shipment.moves
+                    if m.state not in ('assigned', 'done', 'cancel')], {
+                    'planned_date': shipment._move_planned_date,
                     })
 
-    def create(self, values):
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('stock.configuration')
+    @classmethod
+    def create(cls, values):
+        pool = Pool()
+        Sequence = pool.get('ir.sequence')
+        Config = pool.get('stock.configuration')
 
         values = values.copy()
-        config = config_obj.browse(1)
-        values['code'] = sequence_obj.get_id(
-                config.shipment_in_return_sequence.id)
-        shipment_id = super(ShipmentInReturn, self).create(values)
-        self._set_move_planned_date(shipment_id)
-        return shipment_id
+        config = Config(1)
+        values['code'] = Sequence.get_id(
+            config.shipment_in_return_sequence.id)
+        shipment = super(ShipmentInReturn, cls).create(values)
+        cls._set_move_planned_date([shipment])
+        return shipment
 
-    def write(self, ids, values):
-        result = super(ShipmentInReturn, self).write(ids, values)
-        self._set_move_planned_date(ids)
-        return result
+    @classmethod
+    def write(cls, shipments, values):
+        super(ShipmentInReturn, cls).write(shipments, values)
+        cls._set_move_planned_date(shipments)
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, shipments):
         # Cancel before delete
-        self.cancel(ids)
-        for shipment in self.browse(ids):
+        cls.cancel(shipments)
+        for shipment in shipments:
             if shipment.state != 'cancel':
-                self.raise_user_error('delete_cancel', shipment.rec_name)
-        return super(ShipmentInReturn, self).delete(ids)
+                cls.raise_user_error('delete_cancel', shipment.rec_name)
+        super(ShipmentInReturn, cls).delete(shipments)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.moves
+    def draft(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('draft', 'done')], {
                 'state': 'draft',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('waiting')
-    def wait(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
+    def wait(cls, shipments):
+        Move = Pool().get('stock.move')
         for shipment in shipments:
-            move_obj.write([m.id for m in shipment.moves
+            Move.write([m for m in shipment.moves
                     if m.state not in ('cancel', 'draft', 'done')], {
                     'state': 'draft',
-                    'planned_date': shipment.planned_date,
+                    'planned_date': shipment._move_planned_date,
                     })
 
+    @classmethod
     @Workflow.transition('assigned')
-    def assign(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.moves
+    def assign(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('assigned', 'cancel', 'done')], {
                 'state': 'assigned',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('done')
-    def done(self, ids):
-        move_obj = Pool().get('stock.move')
-        date_obj = Pool().get('ir.date')
+    def done(cls, shipments):
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Date = pool.get('ir.date')
 
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.moves
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
-        self.write(ids, {
-                'effective_date': date_obj.today(),
+        cls.write(shipments, {
+                'effective_date': Date.today(),
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.moves
+    def cancel(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('cancel', 'done')], {
                 'state': 'cancel',
                 })
 
+    @classmethod
     @ModelView.button_action('stock.wizard_shipment_in_return_assign')
-    def assign_wizard(self, ids):
+    def assign_wizard(cls, shipments):
         pass
 
+    @classmethod
     @ModelView.button
-    def assign_try(self, ids):
+    def assign_try(cls, shipments):
         pool = Pool()
-        product_obj = pool.get('product.product')
-        uom_obj = pool.get('product.uom')
-        date_obj = pool.get('ir.date')
-        move_obj = pool.get('stock.move')
+        Product = pool.get('product.product')
+        Uom = pool.get('product.uom')
+        Date = pool.get('ir.date')
+        Move = pool.get('stock.move')
 
-        Transaction().cursor.lock(move_obj._table)
+        Transaction().cursor.lock(Move._table)
 
-        shipments = self.browse(ids)
         moves = [m for s in shipments for m in s.moves]
         location_ids = [m.from_location.id for m in moves]
         with Transaction().set_context(
-                stock_date_end=date_obj.today(),
+                stock_date_end=Date.today(),
                 stock_assign=True):
-            pbl = product_obj.products_by_location(location_ids=location_ids,
-                    product_ids=[m.product.id for m in moves])
+            pbl = Product.products_by_location(location_ids=location_ids,
+                product_ids=[m.product.id for m in moves])
 
         for move in moves:
             if move.state != 'draft':
                 continue
             if (move.from_location.id, move.product.id) in pbl:
                 qty_default_uom = pbl[(move.from_location.id, move.product.id)]
-                qty = uom_obj.compute_qty(move.product.default_uom,
-                        qty_default_uom, move.uom, round=False)
+                qty = Uom.compute_qty(move.product.default_uom,
+                    qty_default_uom, move.uom, round=False)
                 if qty < move.quantity:
                     return False
                 pbl[(move.from_location.id, move.product.id)] = (
@@ -739,22 +728,19 @@
                     - qty_default_uom)
             else:
                 return False
-        self.assign(ids)
+        cls.assign(shipments)
         return True
 
+    @classmethod
     @ModelView.button
-    def assign_force(self, ids):
-        self.assign(ids)
-
-ShipmentInReturn()
+    def assign_force(cls, shipments):
+        cls.assign(shipments)
 
 
 class ShipmentOut(Workflow, ModelSQL, ModelView):
     "Customer Shipment"
-    _name = 'stock.shipment.out'
-    _description = __doc__
+    __name__ = 'stock.shipment.out'
     _rec_name = 'code'
-
     effective_date = fields.Date('Effective Date', readonly=True)
     planned_date = fields.Date('Planned Date',
         states={
@@ -777,7 +763,7 @@
         depends=['state', 'outgoing_moves'])
     customer_location = fields.Function(fields.Many2One('stock.location',
             'Customer Location', on_change_with=['customer']),
-        'get_customer_location')
+        'on_change_with_customer_location')
     delivery_address = fields.Many2One('party.address',
         'Delivery Address', required=True,
         states={
@@ -796,10 +782,10 @@
         depends=['state', 'outgoing_moves'])
     warehouse_storage = fields.Function(fields.Many2One('stock.location',
             'Warehouse Storage', on_change_with=['warehouse']),
-        'get_warehouse_storage')
+        'on_change_with_warehouse_storage')
     warehouse_output = fields.Function(fields.Many2One('stock.location',
             'Warehouse Output', on_change_with=['warehouse']),
-        'get_warehouse_output')
+        'on_change_with_warehouse_output')
     outgoing_moves = fields.Function(fields.One2Many('stock.move', None,
             'Outgoing Moves',
             domain=[
@@ -842,14 +828,15 @@
         ('waiting', 'Waiting'),
         ], 'State', readonly=True)
 
-    def __init__(self):
-        super(ShipmentOut, self).__init__()
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentOut, cls).__setup__()
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
                 'delete_cancel': 'Customer Shipment "%s" must be cancelled '\
                     'before deletion!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('draft', 'waiting'),
                 ('waiting', 'assigned'),
                 ('assigned', 'packed'),
@@ -863,7 +850,7 @@
                 ('packed', 'cancel'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': Eval('state').in_(['cancel', 'done']),
                     },
@@ -896,14 +883,15 @@
                 'assign_force': {},
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         old_table = 'stock_packing_out'
         if TableHandler.table_exist(cursor, old_table):
-            TableHandler.table_rename(cursor, old_table, self._table)
+            TableHandler.table_rename(cursor, old_table, cls._table)
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         for field in ('create_uid', 'write_uid', 'delivery_address',
                 'warehouse', 'customer'):
             table.drop_fk(field, table=old_table)
@@ -913,181 +901,144 @@
         # Migration from 2.0:
         created_company = table.column_exist('company')
 
-        super(ShipmentOut, self).init(module_name)
+        super(ShipmentOut, cls).__register__(module_name)
 
         # Migration from 2.0:
-        move_obj = Pool().get('stock.move')
+        Move = Pool().get('stock.move')
         if (not created_company
-                and TableHandler.table_exist(cursor, move_obj._table)):
-            move_obj = Pool().get('stock.move')
+                and TableHandler.table_exist(cursor, Move._table)):
             cursor.execute('SELECT shipment.id, MAX(move.company) '
                 'FROM "%s" AS shipment '
                 'INNER JOIN "%s" AS move ON shipment.id = move.shipment_out '
                 'GROUP BY shipment.id '
                 'ORDER BY MAX(move.company)'
-                % (self._table, move_obj._table))
+                % (cls._table, Move._table))
             for company_id, values in itertools.groupby(cursor.fetchall(),
                     operator.itemgetter(1)):
                 shipment_ids = [x[0] for x in values]
                 for i in range(0, len(shipment_ids), cursor.IN_MAX):
                     sub_ids = shipment_ids[i:i + cursor.IN_MAX]
                     red_sql, red_ids = reduce_ids('id', sub_ids)
-                    cursor.execute('UPDATE "' + self._table + '" '
+                    cursor.execute('UPDATE "' + cls._table + '" '
                         'SET company = %s WHERE ' + red_sql,
                         [company_id] + red_ids)
             table.not_null_action('company', action='add')
 
         # Migration from 1.0 customer_location is no more used
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.drop_column('customer_location', exception=True)
 
         # Add index on create_date
         table.index_action('create_date', action='add')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_warehouse(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(self.warehouse.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_warehouse(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.warehouse.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def on_change_customer(self, values):
-        if not values.get('customer'):
+    def on_change_customer(self):
+        if not self.customer:
             return {'delivery_address': None}
-        party_obj = Pool().get("party.party")
-        address_id = party_obj.address_get(values['customer'], type='delivery')
-        return {'delivery_address': address_id}
+        address = self.customer.address_get(type='delivery')
+        return {'delivery_address': address.id}
 
-    def on_change_with_customer_location(self, values):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        if values.get('customer'):
-            customer = party_obj.browse(values['customer'])
-            return customer.customer_location.id
+    def on_change_with_customer_location(self, name=None):
+        if self.customer:
+            return self.customer.customer_location.id
 
-    def get_customer_location(self, ids, name):
-        locations = {}
-        for shipment in self.browse(ids):
-            locations[shipment.id] = shipment.customer.customer_location.id
-        return locations
+    @classmethod
+    def default_warehouse_storage(cls):
+        warehouse = cls.default_warehouse()
+        if warehouse:
+            return cls(warehouse=warehouse).on_change_with_warehouse_storage()
 
-    def default_warehouse_storage(self):
-        warehouse = self.default_warehouse()
+    def on_change_with_warehouse_storage(self, name=None):
+        if self.warehouse:
+            return self.warehouse.storage_location.id
+
+    @classmethod
+    def default_warehouse_output(cls):
+        warehouse = cls.default_warehouse()
         if warehouse:
-            value = self.on_change_with_warehouse_storage({
-                    'warehouse': warehouse,
-                    })
-            return value
+            return cls(warehouse=warehouse).on_change_with_warehouse_output()
 
-    def on_change_with_warehouse_storage(self, values):
-        pool = Pool()
-        location_obj = pool.get('stock.location')
-        if values.get('warehouse'):
-            warehouse = location_obj.browse(values['warehouse'])
-            return warehouse.storage_location.id
+    def on_change_with_warehouse_output(self, name=None):
+        if self.warehouse:
+            return self.warehouse.output_location.id
 
-    def get_warehouse_storage(self, ids, name):
-        storages = {}
-        for shipment in self.browse(ids):
-            storages[shipment.id] = shipment.warehouse.storage_location.id
-        return storages
+    def get_outgoing_moves(self, name):
+        moves = []
+        for move in self.moves:
+            if move.from_location.id == self.warehouse.output_location.id:
+                moves.append(move.id)
+        return moves
 
-    def default_warehouse_output(self):
-        warehouse = self.default_warehouse()
-        if warehouse:
-            value = self.on_change_with_warehouse_output({
-                    'warehouse': warehouse,
-                    })
-            return value
-
-    def on_change_with_warehouse_output(self, values):
-        pool = Pool()
-        location_obj = pool.get('stock.location')
-        if values.get('warehouse'):
-            warehouse = location_obj.browse(values['warehouse'])
-            return warehouse.output_location.id
-
-    def get_warehouse_output(self, ids, name):
-        outputs = {}
-        for shipment in self.browse(ids):
-            outputs[shipment.id] = shipment.warehouse.output_location.id
-        return outputs
-
-    def get_outgoing_moves(self, ids, name):
-        res = {}
-        for shipment in self.browse(ids):
-            res[shipment.id] = []
-            for move in shipment.moves:
-                if move.from_location.id == \
-                        shipment.warehouse.output_location.id:
-                    res[shipment.id].append(move.id)
-        return res
-
-    def set_outgoing_moves(self, ids, name, value):
+    @classmethod
+    def set_outgoing_moves(cls, shipments, name, value):
         if not value:
             return
-        self.write(ids, {
-            'moves': value,
-            })
+        cls.write(shipments, {
+                'moves': value,
+                })
 
-    def get_inventory_moves(self, ids, name):
-        res = {}
-        for shipment in self.browse(ids):
-            res[shipment.id] = []
-            for move in shipment.moves:
-                if move.to_location.id == \
-                        shipment.warehouse.output_location.id:
-                    res[shipment.id].append(move.id)
-        return res
+    def get_inventory_moves(self, name):
+        moves = []
+        for move in self.moves:
+            if move.to_location.id == self.warehouse.output_location.id:
+                moves.append(move.id)
+        return moves
 
-    def set_inventory_moves(self, ids, name, value):
+    @classmethod
+    def set_inventory_moves(cls, shipments, name, value):
         if not value:
             return
-        self.write(ids, {
-            'moves': value,
-            })
+        cls.write(shipments, {
+                'moves': value,
+                })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments
+    def draft(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments
                 for m in s.inventory_moves + s.outgoing_moves
                 if m.state not in ('draft', 'done')], {
                 'state': 'draft',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('waiting')
-    def wait(self, ids):
+    def wait(cls, shipments):
         """
         Complete inventory moves to match the products and quantities
         that are in the outgoing moves.
         """
-        move_obj = Pool().get('stock.move')
+        Move = Pool().get('stock.move')
 
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.inventory_moves
+        Move.write([m for s in shipments for m in s.inventory_moves
                 if m.state not in ('draft', 'done')], {
                 'state': 'draft',
                 })
-        move_obj.delete([m.id for s in shipments for m in s.inventory_moves
+        Move.delete([m for s in shipments for m in s.inventory_moves
                 if m.state in ('draft', 'cancel')])
 
-        # Re-Browse because moves have been deleted
-        shipments = self.browse(ids)
-
         for shipment in shipments:
             for move in shipment.outgoing_moves:
                 if move.state in ('cancel', 'done'):
                     continue
-                move_obj.create({
+                Move.create({
                         'from_location': \
                             move.shipment_out.warehouse.storage_location.id,
                         'to_location': move.from_location.id,
@@ -1102,17 +1053,19 @@
                         'unit_price': move.unit_price,
                         })
 
+    @classmethod
     @Workflow.transition('assigned')
-    def assign(self, ids):
+    def assign(cls, shipments):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('packed')
-    def pack(self, ids):
-        move_obj = Pool().get('stock.move')
-        uom_obj = Pool().get('product.uom')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.inventory_moves
+    def pack(cls, shipments):
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Uom = pool.get('product.uom')
+        Move.write([m for s in shipments for m in s.inventory_moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
@@ -1123,7 +1076,7 @@
             for move in shipment.outgoing_moves:
                 if move.state == 'cancel':
                     continue
-                quantity = uom_obj.compute_qty(move.uom, move.quantity,
+                quantity = Uom.compute_qty(move.uom, move.quantity,
                         move.product.default_uom, round=False)
                 outgoing_qty.setdefault(move.product.id, 0.0)
                 outgoing_qty[move.product.id] += quantity
@@ -1131,7 +1084,7 @@
             for move in shipment.inventory_moves:
                 if move.state == 'cancel':
                     continue
-                qty_default_uom = uom_obj.compute_qty(move.uom, move.quantity,
+                qty_default_uom = Uom.compute_qty(move.uom, move.quantity,
                     move.product.default_uom, round=False)
                 # Check if the outgoing move doesn't exist already
                 if outgoing_qty.get(move.product.id):
@@ -1143,15 +1096,15 @@
                     else:
                         out_quantity = (qty_default_uom
                             - outgoing_qty[move.product.id])
-                        out_quantity = uom_obj.compute_qty(
+                        out_quantity = Uom.compute_qty(
                             move.product.default_uom, out_quantity, move.uom)
                         outgoing_qty[move.product.id] = 0.0
                 else:
                     out_quantity = move.quantity
 
-                unit_price = uom_obj.compute_price(move.product.default_uom,
+                unit_price = Uom.compute_price(move.product.default_uom,
                         move.product.list_price, move.uom)
-                move_obj.create({
+                Move.create({
                         'from_location': move.to_location.id,
                         'to_location': shipment.customer.customer_location.id,
                         'product': move.product.id,
@@ -1170,152 +1123,152 @@
                 if move.state == 'cancel':
                     continue
                 if outgoing_qty.get(move.product.id, 0.0) > 0.0:
-                    exc_qty = uom_obj.compute_qty(move.product.default_uom,
+                    exc_qty = Uom.compute_qty(move.product.default_uom,
                             outgoing_qty[move.product.id], move.uom)
-                    removed_qty = uom_obj.compute_qty(move.uom,
+                    removed_qty = Uom.compute_qty(move.uom,
                         min(exc_qty, move.quantity), move.product.default_uom,
                         round=False)
-                    move_obj.write(move.id, {
+                    Move.write([move], {
                             'quantity': max(0.0, move.quantity - exc_qty),
                             })
                     outgoing_qty[move.product.id] -= removed_qty
 
-        move_obj.write([m.id for s in shipments for m in s.outgoing_moves
+        Move.write([m for s in shipments for m in s.outgoing_moves
                 if m.state not in ('cancel', 'done')], {
                 'state': 'assigned',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('done')
-    def done(self, ids):
-        move_obj = Pool().get('stock.move')
-        date_obj = Pool().get('ir.date')
+    def done(cls, shipments):
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Date = pool.get('ir.date')
 
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.outgoing_moves
+        Move.write([m for s in shipments for m in s.outgoing_moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
-        self.write(ids, {
-                'effective_date': date_obj.today(),
+        cls.write(shipments, {
+                'effective_date': Date.today(),
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments
+    def cancel(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments
                 for m in s.outgoing_moves + s.inventory_moves
                 if m.state not in ('cancel', 'done')], {
                 'state': 'cancel',
                 })
 
-    def _get_move_planned_date(self, shipment):
+    @property
+    def _move_planned_date(self):
         '''
         Return the planned date for outgoing moves and inventory moves
         '''
-        return shipment.planned_date, shipment.planned_date
+        return self.planned_date, self.planned_date
 
-    def _set_move_planned_date(self, shipment_ids):
+    @classmethod
+    def _set_move_planned_date(self, shipments):
         '''
         Set planned date of moves for the shipments
         '''
-        move_obj = Pool().get('stock.move')
-        if isinstance(shipment_ids, (int, long)):
-            shipment_ids = [shipment_ids]
-        for shipment in self.browse(shipment_ids):
-            dates = self._get_move_planned_date(shipment)
-            outgoing_date, inventory_date = dates
-            move_obj.write([x.id for x in shipment.outgoing_moves
+        Move = Pool().get('stock.move')
+        for shipment in shipments:
+            outgoing_date, inventory_date = shipment._move_planned_date
+            Move.write([x for x in shipment.outgoing_moves
                     if x.state not in ('assigned', 'done', 'cancel')], {
                     'planned_date': outgoing_date,
                     })
-            move_obj.write([x.id for x in shipment.inventory_moves
+            Move.write([x for x in shipment.inventory_moves
                     if x.state not in ('assigned', 'done', 'cancel')], {
                     'planned_date': inventory_date,
                     })
 
-    def create(self, values):
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('stock.configuration')
+    @classmethod
+    def create(cls, values):
+        pool = Pool()
+        Sequence = pool.get('ir.sequence')
+        Config = pool.get('stock.configuration')
 
         values = values.copy()
-        config = config_obj.browse(1)
-        values['code'] = sequence_obj.get_id(config.shipment_out_sequence.id)
-        shipment_id = super(ShipmentOut, self).create(values)
-        self._set_move_planned_date(shipment_id)
-        return shipment_id
+        config = Config(1)
+        values['code'] = Sequence.get_id(config.shipment_out_sequence.id)
+        shipment = super(ShipmentOut, cls).create(values)
+        cls._set_move_planned_date([shipment])
+        return shipment
 
-    def write(self, ids, values):
-        result = super(ShipmentOut, self).write(ids, values)
-        self._set_move_planned_date(ids)
-        return result
+    @classmethod
+    def write(cls, shipments, values):
+        super(ShipmentOut, cls).write(shipments, values)
+        cls._set_move_planned_date(shipments)
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, shipments, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default['inventory_moves'] = None
         default['outgoing_moves'] = None
-        return super(ShipmentOut, self).copy(ids, default=default)
+        return super(ShipmentOut, cls).copy(shipments, default=default)
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, shipments):
         # Cancel before delete
-        self.cancel(ids)
-        for shipment in self.browse(ids):
+        cls.cancel(shipments)
+        for shipment in shipments:
             if shipment.state != 'cancel':
-                self.raise_user_error('delete_cancel', shipment.rec_name)
-        return super(ShipmentOut, self).delete(ids)
+                cls.raise_user_error('delete_cancel', shipment.rec_name)
+        super(ShipmentOut, cls).delete(shipments)
 
-    def _location_amount(self, target_uom, qty_uom, uom_index):
+    @staticmethod
+    def _location_amount(target_uom, qty_uom, uom_index):
         """
         Take a raw list of quantities and uom and convert it to
         the target uom.
         """
-        uom_obj = Pool().get('product.uom')
+        Uom = Pool().get('product.uom')
         res = 0
         for uom, qty in qty_uom:
-            res += uom_obj.compute_qty(uom_index[uom], qty,
+            res += Uom.compute_qty(uom_index[uom], qty,
                     uom_index[target_uom])
         return res
 
+    @classmethod
     @ModelView.button_action('stock.wizard_shipment_out_assign')
-    def assign_wizard(self, ids):
+    def assign_wizard(cls, shipments):
         pass
 
+    @classmethod
     @ModelView.button
-    def assign_try(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        if move_obj.assign_try([m for s in shipments
+    def assign_try(cls, shipments):
+        Move = Pool().get('stock.move')
+        if Move.assign_try([m for s in shipments
                     for m in s.inventory_moves]):
-            self.assign(ids)
+            cls.assign(shipments)
             return True
         else:
             return False
 
+    @classmethod
     @ModelView.button
-    def assign_force(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.inventory_moves
+    def assign_force(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.inventory_moves
                 if m.state not in ('cancel', 'done')], {
                 'state': 'assigned',
                 })
-        self.assign(ids)
-
-ShipmentOut()
+        cls.assign(shipments)
 
 
 class ShipmentOutReturn(Workflow, ModelSQL, ModelView):
     "Customer Return Shipment"
-    _name = 'stock.shipment.out.return'
-    _description = __doc__
+    __name__ = 'stock.shipment.out.return'
     _rec_name = 'code'
-
     effective_date = fields.Date('Effective Date', readonly=True)
     planned_date = fields.Date('Planned Date',
         states={
@@ -1338,7 +1291,7 @@
         depends=['state', 'incoming_moves'])
     customer_location = fields.Function(fields.Many2One('stock.location',
             'Customer Location', on_change_with=['customer']),
-        'get_customer_location')
+        'on_change_with_customer_location')
     delivery_address = fields.Many2One('party.address',
         'Delivery Address', required=True,
         states={
@@ -1357,10 +1310,10 @@
         depends=['state', 'incoming_moves'])
     warehouse_storage = fields.Function(fields.Many2One('stock.location',
             'Warehouse Storage', on_change_with=['warehouse']),
-        'get_warehouse_storage')
+        'on_change_with_warehouse_storage')
     warehouse_input = fields.Function(fields.Many2One('stock.location',
             'Warehouse Input', on_change_with=['warehouse']),
-        'get_warehouse_input')
+        'on_change_with_warehouse_input')
     incoming_moves = fields.Function(fields.One2Many('stock.move', None,
             'Incoming Moves',
             domain=[
@@ -1400,14 +1353,15 @@
         ('received', 'Received'),
         ], 'State', readonly=True)
 
-    def __init__(self):
-        super(ShipmentOutReturn, self).__init__()
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentOutReturn, cls).__setup__()
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
                 'delete_cancel': 'Customer Return Shipment "%s" must be '\
                     'cancelled before deletion!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('draft', 'received'),
                 ('received', 'done'),
                 ('received', 'draf'),
@@ -1415,7 +1369,7 @@
                 ('received', 'cancel'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': Eval('state').in_(['cancel', 'done']),
                     },
@@ -1430,14 +1384,15 @@
                     },
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         old_table = 'stock_packing_out_return'
         if TableHandler.table_exist(cursor, old_table):
-            TableHandler.table_rename(cursor, old_table, self._table)
+            TableHandler.table_rename(cursor, old_table, cls._table)
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         for field in ('create_uid', 'write_uid', 'delivery_address',
                 'warehouse', 'customer'):
             table.drop_fk(field, table=old_table)
@@ -1447,253 +1402,223 @@
         # Migration from 2.0:
         created_company = table.column_exist('company')
 
-        super(ShipmentOutReturn, self).init(module_name)
+        super(ShipmentOutReturn, cls).__register__(module_name)
 
         # Migration from 2.0:
-        move_obj = Pool().get('stock.move')
+        Move = Pool().get('stock.move')
         if (not created_company
-                and TableHandler.table_exist(cursor, move_obj._table)):
+                and TableHandler.table_exist(cursor, Move._table)):
             cursor.execute('SELECT shipment.id, MAX(move.company) '
                 'FROM "%s" AS shipment '
                 'INNER JOIN "%s" AS move '
                 'ON shipment.id = move.shipment_out_return '
                 'GROUP BY shipment.id '
                 'ORDER BY MAX(move.company)'
-                % (self._table, move_obj._table))
+                % (cls._table, Move._table))
             for company_id, values in itertools.groupby(cursor.fetchall(),
                     operator.itemgetter(1)):
                 shipment_ids = [x[0] for x in values]
                 for i in range(0, len(shipment_ids), cursor.IN_MAX):
                     sub_ids = shipment_ids[i:i + cursor.IN_MAX]
                     red_sql, red_ids = reduce_ids('id', sub_ids)
-                    cursor.execute('UPDATE "' + self._table + '" '
+                    cursor.execute('UPDATE "' + cls._table + '" '
                         'SET company = %s WHERE ' + red_sql,
                         [company_id] + red_ids)
             table.not_null_action('company', action='add')
 
         # Add index on create_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action('create_date', action='add')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_warehouse(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(self.warehouse.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_warehouse(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.warehouse.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def on_change_customer(self, values):
-        if not values.get('customer'):
+    def on_change_customer(self):
+        if not self.customer:
             return {'delivery_address': None}
-        party_obj = Pool().get("party.party")
-        address_id = party_obj.address_get(values['customer'], type='delivery')
+        address = self.customer.address_get(type='delivery')
         return {
-                'delivery_address': address_id,
+            'delivery_address': address.id,
             }
 
-    def on_change_with_customer_location(self, values):
-        pool = Pool()
-        party_obj = pool.get('party.party')
-        if values.get('customer'):
-            customer = party_obj.browse(values['customer'])
-            return customer.customer_location.id
+    def on_change_with_customer_location(self, name=None):
+        if self.customer:
+            return self.customer.customer_location.id
 
-    def get_customer_location(self, ids, name):
-        locations = {}
-        for shipment in self.browse(ids):
-            locations[shipment.id] = shipment.customer.customer_location.id
-        return locations
+    @classmethod
+    def default_warehouse_storage(cls):
+        warehouse = cls.default_warehouse()
+        if warehouse:
+            return cls(warehouse=warehouse).on_change_with_warehouse_storage()
 
-    def default_warehouse_storage(self):
-        warehouse = self.default_warehouse()
+    def on_change_with_warehouse_storage(self, name=None):
+        if self.warehouse:
+            return self.warehouse.storage_location.id
+
+    @classmethod
+    def default_warehouse_input(cls):
+        warehouse = cls.default_warehouse()
         if warehouse:
-            value = self.on_change_with_warehouse_storage({
-                    'warehouse': warehouse,
-                    })
-            return value
+            return cls(warehouse=warehouse).on_change_with_warehouse_input()
 
-    def on_change_with_warehouse_storage(self, values):
-        pool = Pool()
-        location_obj = pool.get('stock.location')
-        if values.get('warehouse'):
-            warehouse = location_obj.browse(values['warehouse'])
-            return warehouse.storage_location.id
+    def on_change_with_warehouse_input(self, name=None):
+        if self.warehouse:
+            return self.warehouse.input_location.id
 
-    def get_warehouse_storage(self, ids, name):
-        storages = {}
-        for shipment in self.browse(ids):
-            storages[shipment.id] = shipment.warehouse.storage_location.id
-        return storages
+    def get_incoming_moves(self, name):
+        moves = []
+        for move in self.moves:
+            if move.to_location.id == self.warehouse.input_location.id:
+                moves.append(move.id)
+        return moves
 
-    def default_warehouse_input(self):
-        warehouse = self.default_warehouse()
-        if warehouse:
-            value = self.on_change_with_warehouse_input({
-                    'warehouse': warehouse,
-                    })
-            return value
-
-    def on_change_with_warehouse_input(self, values):
-        pool = Pool()
-        location_obj = pool.get('stock.location')
-        if values.get('warehouse'):
-            warehouse = location_obj.browse(values['warehouse'])
-            return warehouse.input_location.id
-
-    def get_warehouse_input(self, ids, name):
-        inputs = {}
-        for shipment in self.browse(ids):
-            inputs[shipment.id] = shipment.warehouse.input_location.id
-        return inputs
-
-    def get_incoming_moves(self, ids, name):
-        res = {}
-        for shipment in self.browse(ids):
-            res[shipment.id] = []
-            for move in shipment.moves:
-                if move.to_location.id == \
-                        shipment.warehouse.input_location.id:
-                    res[shipment.id].append(move.id)
-        return res
-
-    def set_incoming_moves(self, ids, name, value):
+    @classmethod
+    def set_incoming_moves(cls, shipments, name, value):
         if not value:
             return
-        self.write(ids, {
-            'moves': value,
-            })
+        cls.write(shipments, {
+                'moves': value,
+                })
 
-    def get_inventory_moves(self, ids, name):
-        res = {}
-        for shipment in self.browse(ids):
-            res[shipment.id] = []
-            for move in shipment.moves:
-                if move.from_location.id == \
-                        shipment.warehouse.input_location.id:
-                    res[shipment.id].append(move.id)
-        return res
+    def get_inventory_moves(self, name):
+        moves = []
+        for move in self.moves:
+            if move.from_location.id == self.warehouse.input_location.id:
+                moves.append(move.id)
+        return moves
 
-    def set_inventory_moves(self, ids, name, value):
+    @classmethod
+    def set_inventory_moves(cls, shipments, name, value):
         if not value:
             return
-        self.write(ids, {
-            'moves': value,
-            })
+        cls.write(shipments, {
+                'moves': value,
+                })
 
-    def _get_move_planned_date(self, shipment):
+    def _get_move_planned_date(self):
         '''
         Return the planned date for incoming moves and inventory moves
         '''
-        return shipment.planned_date, shipment.planned_date
+        return self.planned_date, self.planned_date
 
-    def _set_move_planned_date(self, shipment_ids):
+    @classmethod
+    def _set_move_planned_date(cls, shipments):
         '''
         Set planned date of moves for the shipments
         '''
-        move_obj = Pool().get('stock.move')
-        if isinstance(shipment_ids, (int, long)):
-            shipment_ids = [shipment_ids]
-        for shipment in self.browse(shipment_ids):
-            dates = self._get_move_planned_date(shipment)
+        Move = Pool().get('stock.move')
+        for shipment in shipments:
+            dates = shipment._get_move_planned_date()
             incoming_date, inventory_date = dates
-            move_obj.write([x.id for x in shipment.incoming_moves
+            Move.write([x for x in shipment.incoming_moves
                     if x.state not in ('assigned', 'done', 'cancel')], {
                     'planned_date': incoming_date,
                     })
-            move_obj.write([x.id for x in shipment.inventory_moves
+            Move.write([x for x in shipment.inventory_moves
                     if x.state not in ('assigned', 'done', 'cancel')], {
                     'planned_date': inventory_date,
                     })
 
-    def create(self, values):
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('stock.configuration')
+    @classmethod
+    def create(cls, values):
+        pool = Pool()
+        Sequence = pool.get('ir.sequence')
+        Config = pool.get('stock.configuration')
 
         values = values.copy()
-        config = config_obj.browse(1)
-        values['code'] = sequence_obj.get_id(
+        config = Config(1)
+        values['code'] = Sequence.get_id(
                 config.shipment_out_return_sequence.id)
-        shipment_id = super(ShipmentOutReturn, self).create(values)
-        self._set_move_planned_date(shipment_id)
-        return shipment_id
+        shipment = super(ShipmentOutReturn, cls).create(values)
+        cls._set_move_planned_date([shipment])
+        return shipment
 
-    def write(self, ids, values):
-        result = super(ShipmentOutReturn, self).write(ids, values)
-        self._set_move_planned_date(ids)
-        return result
+    @classmethod
+    def write(cls, shipments, values):
+        super(ShipmentOutReturn, cls).write(shipments, values)
+        cls._set_move_planned_date(shipments)
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, shipments, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default['inventory_moves'] = None
         default['incoming_moves'] = None
-        return super(ShipmentOutReturn, self).copy(ids, default=default)
+        return super(ShipmentOutReturn, cls).copy(shipments, default=default)
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, shipments):
         # Cance before delete
-        self.cancel(ids)
-        for shipment in self.browse(ids):
+        cls.cancel(shipments)
+        for shipment in shipments:
             if shipment.state != 'cancel':
-                self.raise_user_error('delete_cancel', shipment.rec_name)
-        return super(ShipmentOutReturn, self).delete(ids)
+                cls.raise_user_error('delete_cancel', shipment.rec_name)
+        super(ShipmentOutReturn, cls).delete(shipments)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.incoming_moves
+    def draft(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.incoming_moves
                 if m.state not in ('draft', 'done')], {
                 'state': 'draft',
                 })
-        move_obj.delete([m.id for s in shipments for m in s.inventory_moves
+        Move.delete([m for s in shipments for m in s.inventory_moves
                 if m.state in ('draft', 'cancel')])
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('received')
-    def receive(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.incoming_moves
+    def receive(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.incoming_moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
-        self.create_inventory_moves(shipments)
+        cls.create_inventory_moves(shipments)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('done')
-    def done(self, ids):
-        move_obj = Pool().get('stock.move')
-        date_obj = Pool().get('ir.date')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.inventory_moves
+    def done(cls, shipments):
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Date = pool.get('ir.date')
+        Move.write([m for s in shipments for m in s.inventory_moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
-        self.write(ids, {
-                'effective_date': date_obj.today(),
+        cls.write(shipments, {
+                'effective_date': Date.today(),
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments
+    def cancel(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments
                 for m in s.incoming_moves + s.inventory_moves
                 if m.state not in ('cancel', 'done')], {
                 'state': 'cancel',
                 })
 
-    def _get_inventory_moves(self, incoming_move):
+    @staticmethod
+    def _get_inventory_moves(incoming_move):
         res = {}
         if incoming_move.quantity <= 0.0:
             return None
@@ -1710,41 +1635,36 @@
         res['company'] = incoming_move.company.id
         return res
 
-    def create_inventory_moves(self, shipments):
+    @classmethod
+    def create_inventory_moves(cls, shipments):
         for shipment in shipments:
             for incoming_move in shipment.incoming_moves:
-                vals = self._get_inventory_moves(incoming_move)
+                vals = cls._get_inventory_moves(incoming_move)
                 if vals:
-                    self.write(shipment.id, {
+                    cls.write([shipment], {
                         'inventory_moves': [('create', vals)],
                         })
 
-ShipmentOutReturn()
-
 
 class AssignShipmentOutAssignFailed(ModelView):
     'Assign Shipment Out'
-    _name = 'stock.shipment.out.assign.failed'
-    _description = __doc__
+    __name__ = 'stock.shipment.out.assign.failed'
+    inventory_moves = fields.Many2Many('stock.move', None, None,
+        'Inventory Moves', readonly=True)
 
-    inventory_moves = fields.Many2Many('stock.move', None, None,
-            'Inventory Moves', readonly=True)
-
-    def default_inventory_moves(self):
-        shipment_out_obj = Pool().get('stock.shipment.out')
+    @staticmethod
+    def default_inventory_moves():
+        ShipmentOut = Pool().get('stock.shipment.out')
         shipment_id = Transaction().context.get('active_id')
         if not shipment_id:
             return []
-        shipment = shipment_out_obj.browse(shipment_id)
+        shipment = ShipmentOut(shipment_id)
         return [x.id for x in shipment.inventory_moves if x.state == 'draft']
 
-AssignShipmentOutAssignFailed()
-
 
 class AssignShipmentOut(Wizard):
     'Assign Shipment Out'
-    _name = 'stock.shipment.out.assign'
-
+    __name__ = 'stock.shipment.out.assign'
     start = StateTransition()
     failed = StateView('stock.shipment.out.assign.failed',
         'stock.shipment_out_assign_failed_view_form', [
@@ -1758,30 +1678,26 @@
             ])
     force = StateTransition()
 
-    def transition_start(self, session):
+    def transition_start(self):
         pool = Pool()
-        shipment_obj = pool.get('stock.shipment.out')
+        Shipment = pool.get('stock.shipment.out')
 
-        if shipment_obj.assign_try([Transaction().context['active_id']]):
+        if Shipment.assign_try([Shipment(Transaction().context['active_id'])]):
             return 'end'
         else:
             return 'failed'
 
-    def transition_force(self, session):
-        shipment_obj = Pool().get('stock.shipment.out')
+    def transition_force(self):
+        Shipment = Pool().get('stock.shipment.out')
 
-        shipment_obj.assign_force([Transaction().context['active_id']])
+        Shipment.assign_force([Shipment(Transaction().context['active_id'])])
         return 'end'
 
-AssignShipmentOut()
-
 
 class ShipmentInternal(Workflow, ModelSQL, ModelView):
     "Internal Shipment"
-    _name = 'stock.shipment.internal'
-    _description = __doc__
+    __name__ = 'stock.shipment.internal'
     _rec_name = 'code'
-
     effective_date = fields.Date('Effective Date', readonly=True)
     planned_date = fields.Date('Planned Date',
         states={
@@ -1838,14 +1754,15 @@
         ('done', 'Done'),
         ], 'State', readonly=True)
 
-    def __init__(self):
-        super(ShipmentInternal, self).__init__()
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ShipmentInternal, cls).__setup__()
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
                 'delete_cancel': 'Internal Shipment "%s" must be cancelled '\
                     'before deletion!',
                 })
-        self._transitions |= set((
+        cls._transitions |= set((
                 ('draft', 'waiting'),
                 ('waiting', 'waiting'),
                 ('waiting', 'assigned'),
@@ -1857,7 +1774,7 @@
                 ('assigned', 'cancel'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': Eval('state').in_(['cancel', 'done']),
                     },
@@ -1888,13 +1805,14 @@
                 'assign_force': {},
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         old_table = 'stock_packing_internal'
         if TableHandler.table_exist(cursor, old_table):
-            TableHandler.table_rename(cursor, old_table, self._table)
-        table = TableHandler(cursor, self, module_name)
+            TableHandler.table_rename(cursor, old_table, cls._table)
+        table = TableHandler(cursor, cls, module_name)
         for field in ('create_uid', 'write_uid', 'from_location',
                 'to_location'):
             table.drop_fk(field, table=old_table)
@@ -1904,174 +1822,172 @@
         # Migration from 2.0:
         created_company = table.column_exist('company')
 
-        super(ShipmentInternal, self).init(module_name)
+        super(ShipmentInternal, cls).__register__(module_name)
 
         # Migration from 2.0:
-        move_obj = Pool().get('stock.move')
+        Move = Pool().get('stock.move')
         if (not created_company
-                and TableHandler.table_exist(cursor, move_obj._table)):
+                and TableHandler.table_exist(cursor, Move._table)):
             cursor.execute('SELECT shipment.id, MAX(move.company) '
                 'FROM "%s" AS shipment '
                 'INNER JOIN "%s" AS move '
                 'ON shipment.id = move.shipment_internal '
                 'GROUP BY shipment.id '
                 'ORDER BY MAX(move.company)'
-                % (self._table, move_obj._table))
+                % (cls._table, Move._table))
             for company_id, values in itertools.groupby(cursor.fetchall(),
                     operator.itemgetter(1)):
                 shipment_ids = [x[0] for x in values]
                 for i in range(0, len(shipment_ids), cursor.IN_MAX):
                     sub_ids = shipment_ids[i:i + cursor.IN_MAX]
                     red_sql, red_ids = reduce_ids('id', sub_ids)
-                    cursor.execute('UPDATE "' + self._table + '" '
+                    cursor.execute('UPDATE "' + cls._table + '" '
                         'SET company = %s WHERE ' + red_sql,
                         [company_id] + red_ids)
             table.not_null_action('company', action='add')
 
         # Add index on create_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action('create_date', action='add')
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def create(self, values):
-        sequence_obj = Pool().get('ir.sequence')
-        config_obj = Pool().get('stock.configuration')
+    @classmethod
+    def create(cls, values):
+        pool = Pool()
+        Sequence = pool.get('ir.sequence')
+        Config = pool.get('stock.configuration')
 
         values = values.copy()
-        config = config_obj.browse(1)
-        values['code'] = sequence_obj.get_id(
+        config = Config(1)
+        values['code'] = Sequence.get_id(
                 config.shipment_internal_sequence.id)
-        return super(ShipmentInternal, self).create(values)
+        return super(ShipmentInternal, cls).create(values)
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(cls, shipments):
         # Cancel before delete
-        self.cancel(ids)
-        for shipment in self.browse(ids):
+        cls.cancel(shipments)
+        for shipment in shipments:
             if shipment.state != 'cancel':
-                self.raise_user_error('delete_cancel', shipment.rec_name)
-        return super(ShipmentInternal, self).delete(ids)
+                cls.raise_user_error('delete_cancel', shipment.rec_name)
+        super(ShipmentInternal, cls).delete(shipments)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.moves
+    def draft(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('draft', 'done')], {
                 'state': 'draft',
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('waiting')
-    def wait(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
+    def wait(cls, shipments):
+        Move = Pool().get('stock.move')
         # First reset state to draft to allow update from and to location
-        move_obj.write([m.id for s in shipments for m in s.moves
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('draft', 'done')], {
                 'state': 'draft',
                 })
         for shipment in shipments:
-            move_obj.write([m.id for m in shipment.moves
+            Move.write([m for m in shipment.moves
                     if m.state != 'done'], {
                 'from_location': shipment.from_location.id,
                 'to_location': shipment.to_location.id,
                 'planned_date': shipment.planned_date,
                 })
 
+    @classmethod
     @Workflow.transition('assigned')
-    def assign(self, ids):
+    def assign(cls, shipments):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('done')
-    def done(self, ids):
-        move_obj = Pool().get('stock.move')
-        date_obj = Pool().get('ir.date')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.moves
+    def done(cls, shipments):
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Date = pool.get('ir.date')
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'done',
                 })
-        self.write(ids, {
-                'effective_date': date_obj.today(),
+        cls.write(shipments, {
+                'effective_date': Date.today(),
                 })
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.moves
+    def cancel(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('cancel', 'done')], {
                 'state': 'cancel',
                 })
 
+    @classmethod
     @ModelView.button_action('stock.wizard_shipment_internal_assign')
-    def assign_wizard(self, ids):
+    def assign_wizard(cls, shipments):
         pass
 
+    @classmethod
     @ModelView.button
-    def assign_try(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        if move_obj.assign_try([m for s in shipments
+    def assign_try(cls, shipments):
+        Move = Pool().get('stock.move')
+        if Move.assign_try([m for s in shipments
                     for m in s.moves]):
-            self.assign(ids)
+            cls.assign(shipments)
             return True
         else:
             return False
 
+    @classmethod
     @ModelView.button
-    def assign_force(self, ids):
-        move_obj = Pool().get('stock.move')
-        shipments = self.browse(ids)
-        move_obj.write([m.id for s in shipments for m in s.moves
+    def assign_force(cls, shipments):
+        Move = Pool().get('stock.move')
+        Move.write([m for s in shipments for m in s.moves
                 if m.state not in ('assigned', 'done')], {
                 'state': 'assigned',
                 })
-        self.assign(ids)
+        cls.assign(shipments)
 
-ShipmentInternal()
 
-
-class Address(ModelSQL, ModelView):
-    _name = 'party.address'
+class Address:
+    __name__ = 'party.address'
     delivery = fields.Boolean('Delivery')
 
-Address()
-
 
 class AssignShipmentInternalAssignFailed(ModelView):
     'Assign Shipment Internal'
-    _name = 'stock.shipment.internal.assign.failed'
-    _description = __doc__
+    __name__ = 'stock.shipment.internal.assign.failed'
+    moves = fields.Many2Many('stock.move', None, None, 'Moves',
+        readonly=True)
 
-    moves = fields.Many2Many('stock.move', None, None, 'Moves',
-            readonly=True)
-
-    def default_moves(self):
-        shipment_internal_obj = Pool().get('stock.shipment.internal')
+    @staticmethod
+    def default_moves():
+        ShipmentInternal = Pool().get('stock.shipment.internal')
         shipment_id = Transaction().context.get('active_id')
         if not shipment_id:
             return []
-        shipment = shipment_internal_obj.browse(shipment_id)
+        shipment = ShipmentInternal(shipment_id)
         return [x.id for x in shipment.moves if x.state == 'draft']
 
-AssignShipmentInternalAssignFailed()
-
 
 class AssignShipmentInternal(Wizard):
     'Assign Shipment Internal'
-    _name = 'stock.shipment.internal.assign'
-
+    __name__ = 'stock.shipment.internal.assign'
     start = StateTransition()
     failed = StateView('stock.shipment.internal.assign.failed',
         'stock.shipment_internal_assign_failed_view_form', [
@@ -2085,47 +2001,41 @@
             ])
     force = StateTransition()
 
-    def transition_start(self, session):
+    def transition_start(self):
         pool = Pool()
-        shipment_obj = pool.get('stock.shipment.internal')
+        Shipment = pool.get('stock.shipment.internal')
 
-        if shipment_obj.assign_try([Transaction().context['active_id']]):
+        if Shipment.assign_try([Shipment(Transaction().context['active_id'])]):
             return 'end'
         else:
             return 'failed'
 
-    def transition_force(self, session):
-        shipment_obj = Pool().get('stock.shipment.internal')
+    def transition_force(self):
+        Shipment = Pool().get('stock.shipment.internal')
 
-        shipment_obj.assign_force([Transaction().context['active_id']])
+        Shipment.assign_force([Shipment(Transaction().context['active_id'])])
         return 'end'
 
-AssignShipmentInternal()
-
 
 class AssignShipmentInReturnAssignFailed(ModelView):
     'Assign Supplier Return Shipment'
-    _name = 'stock.shipment.in.return.assign.failed'
-    _description = __doc__
-
+    __name__ = 'stock.shipment.in.return.assign.failed'
     moves = fields.Many2Many('stock.move', None, None, 'Moves',
             readonly=True)
 
-    def default_moves(self):
-        shipment_internal_obj = Pool().get('stock.shipment.in.return')
+    @staticmethod
+    def default_moves():
+        ShipmentInternal = Pool().get('stock.shipment.in.return')
         shipment_id = Transaction().context.get('active_id')
         if not shipment_id:
             return []
-        shipment = shipment_internal_obj.browse(shipment_id)
+        shipment = ShipmentInternal(shipment_id)
         return [x.id for x in shipment.moves if x.state == 'draft']
 
-AssignShipmentInReturnAssignFailed()
-
 
 class AssignShipmentInReturn(Wizard):
     'Assign Supplier Return Shipment'
-    _name = 'stock.shipment.in.return.assign'
-
+    __name__ = 'stock.shipment.in.return.assign'
     start = StateTransition()
     failed = StateView('stock.shipment.in.return.assign.failed',
         'stock.shipment_in_return_assign_failed_view_form', [
@@ -2139,44 +2049,42 @@
             ])
     force = StateTransition()
 
-    def transition_start(self, session):
+    def transition_start(self):
         pool = Pool()
-        shipment_obj = pool.get('stock.shipment.in.return')
+        Shipment = pool.get('stock.shipment.in.return')
 
-        if shipment_obj.assign_try([Transaction().context['active_id']]):
+        if Shipment.assign_try([Shipment(Transaction().context['active_id'])]):
             return 'end'
         else:
             return 'failed'
 
-    def transition_force(self, session):
-        shipment_obj = Pool().get('stock.shipment.in.return')
+    def transition_force(self):
+        Shipment = Pool().get('stock.shipment.in.return')
 
-        shipment_obj.assign_force([Transaction().context['active_id']])
+        Shipment.assign_force([Shipment(Transaction().context['active_id'])])
         return 'end'
 
-AssignShipmentInReturn()
-
 
 class CreateShipmentOutReturn(Wizard):
     'Create Customer Return Shipment'
-    _name = 'stock.shipment.out.return.create'
-
+    __name__ = 'stock.shipment.out.return.create'
     start = StateAction('stock.act_shipment_out_return_form')
 
-    def __init__(self):
-        super(CreateShipmentOutReturn, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(CreateShipmentOutReturn, cls).__setup__()
+        cls._error_messages.update({
             'shipment_done_title': 'You can not create return shipment',
             'shipment_done_msg': 'The shipment with code %s is not yet sent.',
             })
 
-    def do_start(self, session, action):
+    def do_start(self, action):
         pool = Pool()
-        shipment_out_obj = pool.get('stock.shipment.out')
-        shipment_out_return_obj = pool.get('stock.shipment.out.return')
+        ShipmentOut = pool.get('stock.shipment.out')
+        ShipmentOutReturn = pool.get('stock.shipment.out.return')
 
         shipment_ids = Transaction().context['active_ids']
-        shipment_outs = shipment_out_obj.browse(shipment_ids)
+        shipment_outs = ShipmentOut.browse(shipment_ids)
 
         shipment_out_return_ids = []
         for shipment_out in shipment_outs:
@@ -2197,12 +2105,12 @@
                             'company': move.company.id,
                             }))
             shipment_out_return_ids.append(
-                shipment_out_return_obj.create({
+                ShipmentOutReturn.create({
                     'customer': shipment_out.customer.id,
                     'delivery_address': shipment_out.delivery_address.id,
                     'warehouse': shipment_out.warehouse.id,
                     'incoming_moves': incoming_moves,
-                    })
+                    }).id
                 )
 
         data = {'res_id': shipment_out_return_ids}
@@ -2210,50 +2118,52 @@
             action['views'].reverse()
         return action, data
 
-    def transition_start(self, session):
+    def transition_start(self):
         return 'end'
 
-CreateShipmentOutReturn()
-
 
 class DeliveryNote(CompanyReport):
-    _name = 'stock.shipment.out.delivery_note'
+    'Delivery Note'
+    __name__ = 'stock.shipment.out.delivery_note'
 
-    def parse(self, report, objects, datas, localcontext):
+    @classmethod
+    def parse(cls, report, objects, data, localcontext):
         localcontext['product_name'] = lambda product_id, language: \
-                self.product_name(product_id, language)
-        return super(DeliveryNote, self).parse(report, objects, datas,
+                cls.product_name(product_id, language)
+        return super(DeliveryNote, cls).parse(report, objects, data,
                 localcontext)
 
-    def product_name(self, product_id, language):
-        product_obj = Pool().get('product.product')
+    @classmethod
+    def product_name(cls, product_id, language):
+        Product = Pool().get('product.product')
         with Transaction().set_context(language=language):
-            return product_obj.browse(product_id).rec_name
-
-DeliveryNote()
+            return Product(product_id).rec_name
 
 
 class PickingList(CompanyReport):
-    _name = 'stock.shipment.out.picking_list'
+    'Picking List'
+    __name__ = 'stock.shipment.out.picking_list'
 
-    def parse(self, report, objects, datas, localcontext):
-        compare_context = self.get_compare_context(report, objects, datas)
+    @classmethod
+    def parse(cls, report, objects, data, localcontext):
+        compare_context = cls.get_compare_context(report, objects, data)
 
         sorted_moves = {}
         for shipment in objects:
             sorted_moves[shipment.id] = sorted(
                 shipment.inventory_moves,
-                lambda x, y: cmp(self.get_compare_key(x, compare_context),
-                    self.get_compare_key(y, compare_context))
+                lambda x, y: cmp(cls.get_compare_key(x, compare_context),
+                    cls.get_compare_key(y, compare_context))
                 )
 
         localcontext['moves'] = sorted_moves
 
-        return super(PickingList, self).parse(report, objects, datas,
-                localcontext)
+        return super(PickingList, cls).parse(report, objects, data,
+            localcontext)
 
-    def get_compare_context(self, report, objects, datas):
-        location_obj = Pool().get('stock.location')
+    @staticmethod
+    def get_compare_context(report, objects, data):
+        Location = Pool().get('stock.location')
         from_location_ids = set()
         to_location_ids = set()
         for obj in objects:
@@ -2261,44 +2171,46 @@
                 from_location_ids.add(move.from_location.id)
                 to_location_ids.add(move.to_location.id)
 
-        from_location_ids = location_obj.search(list(from_location_ids))
-        to_location_ids = location_obj.search(list(to_location_ids))
+        from_locations = Location.search(list(from_location_ids))
+        to_locations = Location.search(list(to_location_ids))
 
         return {
-            'from_location_ids': from_location_ids,
-            'to_location_ids': to_location_ids,
+            'from_location_ids': [l.id for l in from_locations],
+            'to_location_ids': [l.id for l in to_locations],
             }
 
-    def get_compare_key(self, move, compare_context):
+    @staticmethod
+    def get_compare_key(move, compare_context):
         from_location_ids = compare_context['from_location_ids']
         to_location_ids = compare_context['to_location_ids']
         return [from_location_ids.index(move.from_location.id),
                 to_location_ids.index(move.to_location.id)]
 
-PickingList()
-
 
 class SupplierRestockingList(CompanyReport):
-    _name = 'stock.shipment.in.restocking_list'
+    'Supplier Restocking List'
+    __name__ = 'stock.shipment.in.restocking_list'
 
-    def parse(self, report, objects, datas, localcontext):
-        compare_context = self.get_compare_context(report, objects, datas)
+    @classmethod
+    def parse(cls, report, objects, data, localcontext):
+        compare_context = cls.get_compare_context(report, objects, data)
 
         sorted_moves = {}
         for shipment in objects:
             sorted_moves[shipment.id] = sorted(
                 shipment.inventory_moves,
-                lambda x, y: cmp(self.get_compare_key(x, compare_context),
-                    self.get_compare_key(y, compare_context))
+                lambda x, y: cmp(cls.get_compare_key(x, compare_context),
+                    cls.get_compare_key(y, compare_context))
                 )
 
         localcontext['moves'] = sorted_moves
 
-        return super(SupplierRestockingList, self).parse(report, objects,
-                datas, localcontext)
+        return super(SupplierRestockingList, cls).parse(report, objects,
+                data, localcontext)
 
-    def get_compare_context(self, report, objects, datas):
-        location_obj = Pool().get('stock.location')
+    @staticmethod
+    def get_compare_context(report, objects, data):
+        Location = Pool().get('stock.location')
         from_location_ids = set()
         to_location_ids = set()
         for obj in objects:
@@ -2306,44 +2218,46 @@
                 from_location_ids.add(move.from_location.id)
                 to_location_ids.add(move.to_location.id)
 
-        from_location_ids = location_obj.search(list(from_location_ids))
-        to_location_ids = location_obj.search(list(to_location_ids))
+        from_locations = Location.search(list(from_location_ids))
+        to_locations = Location.search(list(to_location_ids))
 
         return {
-            'from_location_ids': from_location_ids,
-            'to_location_ids': to_location_ids,
+            'from_location_ids': [l.id for l in from_locations],
+            'to_location_ids': [l.id for l in to_locations],
             }
 
-    def get_compare_key(self, move, compare_context):
+    @staticmethod
+    def get_compare_key(move, compare_context):
         from_location_ids = compare_context['from_location_ids']
         to_location_ids = compare_context['to_location_ids']
         return [from_location_ids.index(move.from_location.id),
                 to_location_ids.index(move.to_location.id)]
 
-SupplierRestockingList()
-
 
 class CustomerReturnRestockingList(CompanyReport):
-    _name = 'stock.shipment.out.return.restocking_list'
+    'Customer Return Restocking List'
+    __name__ = 'stock.shipment.out.return.restocking_list'
 
-    def parse(self, report, objects, datas, localcontext):
-        compare_context = self.get_compare_context(report, objects, datas)
+    @classmethod
+    def parse(cls, report, objects, data, localcontext):
+        compare_context = cls.get_compare_context(report, objects, data)
 
         sorted_moves = {}
         for shipment in objects:
             sorted_moves[shipment.id] = sorted(
                 shipment.inventory_moves,
-                lambda x, y: cmp(self.get_compare_key(x, compare_context),
-                    self.get_compare_key(y, compare_context))
+                lambda x, y: cmp(cls.get_compare_key(x, compare_context),
+                    cls.get_compare_key(y, compare_context))
                 )
 
         localcontext['moves'] = sorted_moves
 
-        return super(CustomerReturnRestockingList, self).parse(report,
-                objects, datas, localcontext)
+        return super(CustomerReturnRestockingList, cls).parse(report,
+                objects, data, localcontext)
 
-    def get_compare_context(self, report, objects, datas):
-        location_obj = Pool().get('stock.location')
+    @staticmethod
+    def get_compare_context(report, objects, data):
+        Location = Pool().get('stock.location')
         from_location_ids = set()
         to_location_ids = set()
         for obj in objects:
@@ -2351,44 +2265,46 @@
                 from_location_ids.add(move.from_location.id)
                 to_location_ids.add(move.to_location.id)
 
-        from_location_ids = location_obj.search(list(from_location_ids))
-        to_location_ids = location_obj.search(list(to_location_ids))
+        from_locations = Location.search(list(from_location_ids))
+        to_locations = Location.search(list(to_location_ids))
 
         return {
-            'from_location_ids': from_location_ids,
-            'to_location_ids': to_location_ids,
+            'from_location_ids': [l.id for l in from_locations],
+            'to_location_ids': [l.id for l in to_locations],
             }
 
-    def get_compare_key(self, move, compare_context):
+    @staticmethod
+    def get_compare_key(move, compare_context):
         from_location_ids = compare_context['from_location_ids']
         to_location_ids = compare_context['to_location_ids']
         return [from_location_ids.index(move.from_location.id),
                 to_location_ids.index(move.to_location.id)]
 
-CustomerReturnRestockingList()
-
 
 class InteralShipmentReport(CompanyReport):
-    _name = 'stock.shipment.internal.report'
+    'Interal Shipment Report'
+    __name__ = 'stock.shipment.internal.report'
 
-    def parse(self, report, objects, datas, localcontext=None):
-        compare_context = self.get_compare_context(report, objects, datas)
+    @classmethod
+    def parse(cls, report, objects, data, localcontext=None):
+        compare_context = cls.get_compare_context(report, objects, data)
 
         sorted_moves = {}
         for shipment in objects:
             sorted_moves[shipment.id] = sorted(
                 shipment.moves,
-                lambda x, y: cmp(self.get_compare_key(x, compare_context),
-                    self.get_compare_key(y, compare_context))
+                lambda x, y: cmp(cls.get_compare_key(x, compare_context),
+                    cls.get_compare_key(y, compare_context))
                 )
 
         localcontext['moves'] = sorted_moves
 
-        return super(InteralShipmentReport, self).parse(report, objects,
-                datas, localcontext)
+        return super(InteralShipmentReport, cls).parse(report, objects,
+            data, localcontext)
 
-    def get_compare_context(self, report, objects, datas):
-        location_obj = Pool().get('stock.location')
+    @staticmethod
+    def get_compare_context(report, objects, data):
+        Location = Pool().get('stock.location')
         from_location_ids = set()
         to_location_ids = set()
         for obj in objects:
@@ -2396,18 +2312,17 @@
                 from_location_ids.add(move.from_location.id)
                 to_location_ids.add(move.to_location.id)
 
-        from_location_ids = location_obj.search(list(from_location_ids))
-        to_location_ids = location_obj.search(list(to_location_ids))
+        from_locations = Location.search(list(from_location_ids))
+        to_locations = Location.search(list(to_location_ids))
 
         return {
-            'from_location_ids': from_location_ids,
-            'to_location_ids': to_location_ids,
+            'from_location_ids': [l.id for l in from_locations],
+            'to_location_ids': [l.id for l in to_locations],
             }
 
-    def get_compare_key(self, move, compare_context):
+    @staticmethod
+    def get_compare_key(move, compare_context):
         from_location_ids = compare_context['from_location_ids']
         to_location_ids = compare_context['to_location_ids']
         return [from_location_ids.index(move.from_location.id),
                 to_location_ids.index(move.to_location.id)]
-
-InteralShipmentReport()
diff -r 091f89c47ad9 trytond/modules/stock/tests/test_stock.py
--- a/trytond/modules/stock/tests/test_stock.py	Mon Sep 10 15:28:38 2012 +0200
+++ b/trytond/modules/stock/tests/test_stock.py	Mon Sep 24 20:36:42 2012 +0200
@@ -56,58 +56,53 @@
         Test Move.internal_quantity.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            category_id = self.category.create({
-                'name': 'Test Move.internal_quantity',
-                })
-            kg_id, = self.uom.search([('name', '=', 'Kilogram')])
-            g_id, = self.uom.search([('name', '=', 'Gram')])
-            product_id = self.product.create({
+            category = self.category.create({
+                    'name': 'Test Move.internal_quantity',
+                    })
+            kg, = self.uom.search([('name', '=', 'Kilogram')])
+            g, = self.uom.search([('name', '=', 'Gram')])
+            product = self.product.create({
                 'name': 'Test Move.internal_quantity',
                 'type': 'goods',
-                'list_price': Decimal(0),
+                'list_price': Decimal(1),
                 'cost_price': Decimal(0),
-                'category': category_id,
+                'category': category.id,
                 'cost_price_method': 'fixed',
-                'default_uom': kg_id,
+                'default_uom': kg.id,
                 })
-            supplier_id, = self.location.search([('code', '=', 'SUP')])
-            storage_id, = self.location.search([('code', '=', 'STO')])
-            company_id, = self.company.search([('name', '=', 'B2CK')])
-            currency_id = self.company.read(company_id,
-                    ['currency'])['currency']
-            self.user.write(USER, {
-                'main_company': company_id,
-                'company': company_id,
+            supplier, = self.location.search([('code', '=', 'SUP')])
+            storage, = self.location.search([('code', '=', 'STO')])
+            company, = self.company.search([('name', '=', 'B2CK')])
+            currency = company.currency
+            self.user.write([self.user(USER)], {
+                'main_company': company.id,
+                'company': company.id,
                 })
 
             tests = [
-                (kg_id, 10, 10),
-                (g_id, 100, 0.1),
-                (g_id, 1, 0),  # rounded
+                (kg, 10, 10),
+                (g, 100, 0.1),
+                (g, 1, 0),  # rounded
             ]
-            for uom_id, quantity, internal_quantity in tests:
-                move_id = self.move.create({
-                    'product': product_id,
-                    'uom': uom_id,
+            for uom, quantity, internal_quantity in tests:
+                move = self.move.create({
+                    'product': product.id,
+                    'uom': uom.id,
                     'quantity': quantity,
-                    'from_location': supplier_id,
-                    'to_location': storage_id,
-                    'company': company_id,
+                    'from_location': supplier.id,
+                    'to_location': storage.id,
+                    'company': company.id,
                     'unit_price': Decimal('1'),
-                    'currency': currency_id,
+                    'currency': currency.id,
                     })
-                self.assertEqual(self.move.read(move_id,
-                    ['internal_quantity'])['internal_quantity'],
-                    internal_quantity)
+                self.assertEqual(move.internal_quantity, internal_quantity)
 
-                for uom_id, quantity, internal_quantity in tests:
-                    self.move.write(move_id, {
-                        'uom': uom_id,
+                for uom, quantity, internal_quantity in tests:
+                    self.move.write([move], {
+                        'uom': uom.id,
                         'quantity': quantity,
                         })
-                    self.assertEqual(self.move.read(move_id,
-                        ['internal_quantity'])['internal_quantity'],
-                        internal_quantity)
+                    self.assertEqual(move.internal_quantity, internal_quantity)
 
     def test0020products_by_location(self):
         '''
@@ -115,113 +110,112 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            category_id = self.category.create({
-                'name': 'Test products_by_location',
-                })
-            kg_id, = self.uom.search([('name', '=', 'Kilogram')])
-            g_id, = self.uom.search([('name', '=', 'Gram')])
-            product_id = self.product.create({
+            category = self.category.create({
+                    'name': 'Test products_by_location',
+                    })
+            kg, = self.uom.search([('name', '=', 'Kilogram')])
+            g, = self.uom.search([('name', '=', 'Gram')])
+            product = self.product.create({
                 'name': 'Test products_by_location',
                 'type': 'goods',
                 'list_price': Decimal(0),
                 'cost_price': Decimal(0),
-                'category': category_id,
+                'category': category.id,
                 'cost_price_method': 'fixed',
-                'default_uom': kg_id,
+                'default_uom': kg.id,
                 })
-            supplier_id, = self.location.search([('code', '=', 'SUP')])
-            customer_id, = self.location.search([('code', '=', 'CUS')])
-            storage_id, = self.location.search([('code', '=', 'STO')])
-            company_id, = self.company.search([('name', '=', 'B2CK')])
-            currency_id = self.company.read(company_id,
-                    ['currency'])['currency']
-            self.user.write(USER, {
-                'main_company': company_id,
-                'company': company_id,
+            supplier, = self.location.search([('code', '=', 'SUP')])
+            customer, = self.location.search([('code', '=', 'CUS')])
+            storage, = self.location.search([('code', '=', 'STO')])
+            company, = self.company.search([('name', '=', 'B2CK')])
+            currency = company.currency
+            self.user.write([self.user(USER)], {
+                'main_company': company.id,
+                'company': company.id,
                 })
 
             today = datetime.date.today()
 
             self.move.create({
-                'product': product_id,
-                'uom': kg_id,
+                'product': product.id,
+                'uom': kg.id,
                 'quantity': 5,
-                'from_location': supplier_id,
-                'to_location': storage_id,
+                'from_location': supplier.id,
+                'to_location': storage.id,
                 'planned_date': today + relativedelta(days=-5),
                 'effective_date': today + relativedelta(days=-5),
                 'state': 'done',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
             self.move.create({
-                'product': product_id,
-                'uom': kg_id,
+                'product': product.id,
+                'uom': kg.id,
                 'quantity': 1,
-                'from_location': supplier_id,
-                'to_location': storage_id,
+                'from_location': supplier.id,
+                'to_location': storage.id,
                 'planned_date': today + relativedelta(days=-4),
                 'state': 'draft',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
             self.move.create({
-                'product': product_id,
-                'uom': kg_id,
+                'product': product.id,
+                'uom': kg.id,
                 'quantity': 1,
-                'from_location': storage_id,
-                'to_location': customer_id,
+                'from_location': storage.id,
+                'to_location': customer.id,
                 'planned_date': today,
                 'effective_date': today,
                 'state': 'done',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
             self.move.create({
-                'product': product_id,
-                'uom': kg_id,
+                'product': product.id,
+                'uom': kg.id,
                 'quantity': 1,
-                'from_location': storage_id,
-                'to_location': customer_id,
+                'from_location': storage.id,
+                'to_location': customer.id,
                 'planned_date': today,
                 'state': 'draft',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
             self.move.create({
-                'product': product_id,
-                'uom': kg_id,
+                'product': product.id,
+                'uom': kg.id,
                 'quantity': 2,
-                'from_location': storage_id,
-                'to_location': customer_id,
+                'from_location': storage.id,
+                'to_location': customer.id,
                 'planned_date': today + relativedelta(days=5),
                 'state': 'draft',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
             self.move.create({
-                'product': product_id,
-                'uom': kg_id,
+                'product': product.id,
+                'uom': kg.id,
                 'quantity': 5,
-                'from_location': supplier_id,
-                'to_location': storage_id,
+                'from_location': supplier.id,
+                'to_location': storage.id,
                 'planned_date': today + relativedelta(days=7),
                 'state': 'draft',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
 
             products_by_location = partial(self.product.products_by_location,
-                    [storage_id], [product_id])
+                    [storage.id], [product.id])
             products_by_location_zero = partial(
                     self.product.products_by_location,
-                    [storage_id], [product_id], skip_zero=False)
+                    [storage.id], [product.id], skip_zero=False)
 
             tests = [
                 ({'stock_date_end': today + relativedelta(days=-6),
@@ -284,10 +278,10 @@
                         if not quantity:
                             self.assertEqual(products_by_location(), {})
                             self.assertEqual(products_by_location_zero(),
-                                    {(storage_id, product_id): quantity})
+                                    {(storage.id, product.id): quantity})
                         else:
                             self.assertEqual(products_by_location(),
-                                    {(storage_id, product_id): quantity})
+                                    {(storage.id, product.id): quantity})
 
             test_products_by_location()
 
@@ -300,27 +294,27 @@
             ]
 
             self.move.create({
-                'product': product_id,
-                'uom': g_id,
+                'product': product.id,
+                'uom': g.id,
                 'quantity': 1,
-                'from_location': supplier_id,
-                'to_location': storage_id,
+                'from_location': supplier.id,
+                'to_location': storage.id,
                 'planned_date': today + relativedelta(days=-5),
                 'effective_date': today + relativedelta(days=-5),
                 'state': 'done',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
             # Nothing should change when adding a small quantity
             test_products_by_location()
 
             for period_date in periods:
-                period_id = self.period.create({
+                period = self.period.create({
                     'date': period_date,
-                    'company': company_id,
-                })
-                self.period.close([period_id])
+                    'company': company.id,
+                        })
+                self.period.close([period])
                 test_products_by_location()
 
     def test0030period(self):
@@ -328,144 +322,139 @@
         Test period.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            category_id = self.category.create({
-                'name': 'Test period',
-                })
-            unit_id, = self.uom.search([('name', '=', 'Unit')])
-            product_id = self.product.create({
+            category = self.category.create({
+                    'name': 'Test period',
+                    })
+            unit, = self.uom.search([('name', '=', 'Unit')])
+            product = self.product.create({
                 'name': 'Test period',
                 'type': 'goods',
-                'category': category_id,
+                'category': category.id,
                 'cost_price_method': 'fixed',
-                'default_uom': unit_id,
+                'default_uom': unit.id,
                 'list_price': Decimal(0),
                 'cost_price': Decimal(0),
                 })
-            supplier_id, = self.location.search([('code', '=', 'SUP')])
-            customer_id, = self.location.search([('code', '=', 'CUS')])
-            storage_id, = self.location.search([('code', '=', 'STO')])
-            company_id, = self.company.search([('name', '=', 'B2CK')])
-            currency_id = self.company.read(company_id,
-                    ['currency'])['currency']
-            self.user.write(USER, {
-                'main_company': company_id,
-                'company': company_id,
+            supplier, = self.location.search([('code', '=', 'SUP')])
+            customer, = self.location.search([('code', '=', 'CUS')])
+            storage, = self.location.search([('code', '=', 'STO')])
+            company, = self.company.search([('name', '=', 'B2CK')])
+            currency = company.currency
+            self.user.write([self.user(USER)], {
+                'main_company': company.id,
+                'company': company.id,
                 })
 
             today = datetime.date.today()
 
             self.move.create({
-                'product': product_id,
-                'uom': unit_id,
+                'product': product.id,
+                'uom': unit.id,
                 'quantity': 10,
-                'from_location': supplier_id,
-                'to_location': storage_id,
+                'from_location': supplier.id,
+                'to_location': storage.id,
                 'planned_date': today + relativedelta(days=-5),
                 'effective_date': today + relativedelta(days=-5),
                 'state': 'done',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
             self.move.create({
-                'product': product_id,
-                'uom': unit_id,
+                'product': product.id,
+                'uom': unit.id,
                 'quantity': 15,
-                'from_location': supplier_id,
-                'to_location': storage_id,
+                'from_location': supplier.id,
+                'to_location': storage.id,
                 'planned_date': today + relativedelta(days=-4),
                 'effective_date': today + relativedelta(days=-4),
                 'state': 'done',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
             self.move.create({
-                'product': product_id,
-                'uom': unit_id,
+                'product': product.id,
+                'uom': unit.id,
                 'quantity': 5,
-                'from_location': storage_id,
-                'to_location': customer_id,
+                'from_location': storage.id,
+                'to_location': customer.id,
                 'planned_date': today + relativedelta(days=-3),
                 'effective_date': today + relativedelta(days=-3),
                 'state': 'done',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
 
             tests = [
                 (-5, {
-                    supplier_id: -10,
-                    storage_id: 10,
+                    supplier.id: -10,
+                    storage.id: 10,
                 }),
                 (-3, {
-                    supplier_id: -25,
-                    storage_id: 20,
-                    customer_id: 5,
+                    supplier.id: -25,
+                    storage.id: 20,
+                    customer.id: 5,
                 })
             ]
 
             for days, quantities in tests:
-                period_id = self.period.create({
+                period = self.period.create({
                     'date': today + relativedelta(days=days),
-                    'company': company_id,
+                    'company': company.id,
                 })
-                self.period.close([period_id])
+                self.period.close([period])
 
-                period = self.period.read(period_id, ['state', 'caches'])
-                self.assertEqual(period['state'], 'closed')
+                self.assertEqual(period.state, 'closed')
 
-                cache_ids = period['caches']
-                caches = self.cache.read(cache_ids,
-                    ['location', 'product', 'internal_quantity'])
+                caches = period.caches
                 for cache in caches:
-                    location_id = cache['location']
-                    self.assertEqual(cache['product'], product_id)
-                    self.assertEqual(cache['internal_quantity'],
-                        quantities[location_id])
+                    self.assertEqual(cache.product, product)
+                    self.assertEqual(cache.internal_quantity,
+                        quantities[cache.location.id])
 
             # Test check_period_closed
             self.move.create({
-                'product': product_id,
-                'uom': unit_id,
+                'product': product.id,
+                'uom': unit.id,
                 'quantity': 10,
-                'from_location': supplier_id,
-                'to_location': storage_id,
+                'from_location': supplier.id,
+                'to_location': storage.id,
                 'planned_date': today,
                 'effective_date': today,
                 'state': 'done',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
 
             self.assertRaises(Exception, self.move.create, {
-                'product': product_id,
-                'uom': unit_id,
+                'product': product.id,
+                'uom': unit.id,
                 'quantity': 10,
-                'from_location': supplier_id,
-                'to_location': storage_id,
+                'from_location': supplier.id,
+                'to_location': storage.id,
                 'planned_date': today + relativedelta(days=-5),
                 'effective_date': today + relativedelta(days=-5),
                 'state': 'done',
-                'company': company_id,
+                'company': company.id,
                 'unit_price': Decimal('1'),
-                'currency': currency_id,
+                'currency': currency.id,
                 })
 
             # Test close period check
-            period_id = self.period.create({
+            period = self.period.create({
                 'date': today,
-                'company': company_id,
+                'company': company.id,
             })
-            self.assertRaises(Exception, self.period.close, [period_id])
+            self.assertRaises(Exception, self.period.close, [period])
 
-            period_id = self.period.create({
+            period = self.period.create({
                 'date': today + relativedelta(days=1),
-                'company': company_id,
+                'company': company.id,
             })
-            self.assertRaises(Exception, self.period.close, [period_id])
+            self.assertRaises(Exception, self.period.close, [period])
 
 
 def doctest_dropdb(test):
diff -r 4d2ecbb9bb2a trytond/modules/stock_forecast/__init__.py
--- a/trytond/modules/stock_forecast/__init__.py	Mon Sep 10 15:28:40 2012 +0200
+++ b/trytond/modules/stock_forecast/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,4 +1,18 @@
 #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.pool import Pool
 from .forecast import *
+
+
+def register():
+    Pool.register(
+        Forecast,
+        ForecastLine,
+        ForecastLineMove,
+        ForecastCompleteAsk,
+        ForecastCompleteChoose,
+        module='stock_forecast', type_='model')
+    Pool.register(
+        ForecastComplete,
+        module='stock_forecast', type_='wizard')
diff -r 4d2ecbb9bb2a trytond/modules/stock_forecast/forecast.py
--- a/trytond/modules/stock_forecast/forecast.py	Mon Sep 10 15:28:40 2012 +0200
+++ b/trytond/modules/stock_forecast/forecast.py	Mon Sep 24 20:36:43 2012 +0200
@@ -11,6 +11,9 @@
 from trytond.pool import Pool
 from trytond.tools import reduce_ids
 
+__all__ = ['Forecast', 'ForecastLine', 'ForecastLineMove',
+    'ForecastCompleteAsk', 'ForecastCompleteChoose', 'ForecastComplete']
+
 STATES = {
     'readonly': Not(Equal(Eval('state'), 'draft')),
 }
@@ -19,10 +22,8 @@
 
 class Forecast(Workflow, ModelSQL, ModelView):
     "Stock Forecast"
-    _name = "stock.forecast"
-    _description = __doc__
+    __name__ = "stock.forecast"
     _rec_name = 'warehouse'
-
     warehouse = fields.Many2One(
         'stock.location', 'Location', required=True,
         domain=[('type', '=', 'warehouse')], states={
@@ -53,31 +54,32 @@
         ('cancel', 'Cancel'),
         ], 'State', readonly=True, select=True)
 
-    def __init__(self):
-        super(Forecast, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Forecast, cls).__setup__()
+        cls._sql_constraints += [
             ('check_from_to_date',
              'CHECK(to_date >= from_date)',
              '"To Date" must be greater than "From Date"!'),
             ]
-        self._constraints += [
+        cls._constraints += [
             ('check_date_overlap', 'date_overlap'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
                 'date_overlap': 'You can not create forecasts for the same ' \
                     'locations with overlapping dates',
                 'delete_cancel': 'Forecast "%s" must be cancelled before '\
                     'deletion!',
                 })
-        self._order.insert(0, ('from_date', 'DESC'))
-        self._order.insert(1, ('warehouse', 'ASC'))
-        self._transitions |= set((
+        cls._order.insert(0, ('from_date', 'DESC'))
+        cls._order.insert(1, ('warehouse', 'ASC'))
+        cls._transitions |= set((
                 ('draft', 'done'),
                 ('draft', 'cancel'),
                 ('done', 'draft'),
                 ('cancel', 'draft'),
                 ))
-        self._buttons.update({
+        cls._buttons.update({
                 'cancel': {
                     'invisible': Eval('state') != 'draft',
                     },
@@ -92,18 +94,19 @@
                     },
                 })
 
-    def init(self, module_name):
-        location_obj = Pool().get('stock.location')
+    @classmethod
+    def __register__(cls, module_name):
+        Location = Pool().get('stock.location')
         cursor = Transaction().cursor
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         migrate_warehouse = (not table.column_exist('warehouse')
             and table.column_exist('location'))
 
-        super(Forecast, self).init(module_name)
+        super(Forecast, cls).__register__(module_name)
 
         # Add index on create_date
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action('create_date', action='add')
 
         if migrate_warehouse:
@@ -114,149 +117,138 @@
                     return location.id
                 elif location.parent:
                     return find_warehouse(location.parent)
-            cursor.execute('SELECT id, location FROM "%s"' % self._table)
+            cursor.execute('SELECT id, location FROM "%s"' % cls._table)
             for forecast_id, location_id in cursor.fetchall():
                 warehouse_id = location_id  # default fallback
                 if location_id in location2warehouse:
                     warehouse_id = location2warehouse[location_id]
                 else:
-                    location = location_obj.browse(location_id)
+                    location = Location(location_id)
                     warehouse_id = find_warehouse(location) or location_id
                     location2warehouse[location_id] = warehouse_id
                 cursor.execute('UPDATE "%s" SET warehouse = %%s '
-                    'WHERE id = %%s' % self._table,
+                    'WHERE id = %%s' % cls._table,
                     (warehouse_id, forecast_id))
             table.not_null_action('warehouse',
-                action=self.warehouse.required and 'add' or 'remove')
+                action=cls.warehouse.required and 'add' or 'remove')
             table.drop_column('location', True)
 
         # Migration from 2.0 delete stock moves
-        forecast_ids = self.search([])
-        self.delete_moves(forecast_ids)
+        forecasts = cls.search([])
+        cls.delete_moves(forecasts)
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_destination(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(
-                self.destination.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_destination(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.destination.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def check_date_overlap(self, ids):
+    def check_date_overlap(self):
         cursor = Transaction().cursor
-        for forecast in self.browse(ids):
-            if forecast.state != 'done':
-                continue
-            cursor.execute('SELECT id ' \
-                    'FROM stock_forecast ' \
-                    'WHERE ((from_date <= %s AND to_date >= %s) ' \
-                            'OR (from_date <= %s AND to_date >= %s) ' \
-                            'OR (from_date >= %s AND to_date <= %s)) ' \
-                        'AND warehouse = %s ' \
-                        'AND destination = %s ' \
-                        'AND state = \'done\' ' \
-                        'AND company = %s '
-                        'AND id != %s',
-                    (forecast.from_date, forecast.from_date,
-                     forecast.to_date, forecast.to_date,
-                     forecast.from_date, forecast.to_date,
-                     forecast.warehouse.id, forecast.destination.id,
-                     forecast.company.id, forecast.id))
-            rowcount = cursor.rowcount
-            if rowcount == -1 or rowcount is None:
-                rowcount = len(cursor.fetchall())
-            if rowcount:
-                return False
+        if self.state != 'done':
+            return True
+        cursor.execute('SELECT id ' \
+                'FROM stock_forecast ' \
+                'WHERE ((from_date <= %s AND to_date >= %s) ' \
+                        'OR (from_date <= %s AND to_date >= %s) ' \
+                        'OR (from_date >= %s AND to_date <= %s)) ' \
+                    'AND warehouse = %s ' \
+                    'AND destination = %s ' \
+                    'AND state = \'done\' ' \
+                    'AND company = %s '
+                    'AND id != %s',
+                (self.from_date, self.from_date,
+                 self.to_date, self.to_date,
+                 self.from_date, self.to_date,
+                 self.warehouse.id, self.destination.id,
+                 self.company.id, self.id))
+        rowcount = cursor.rowcount
+        if rowcount == -1 or rowcount is None:
+            rowcount = len(cursor.fetchall())
+        if rowcount:
+            return False
         return True
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
+    @classmethod
+    def delete(self, forecasts):
         # Cancel before delete
-        self.cancel(ids)
-        for forecast in self.browse(ids):
+        self.cancel(forecasts)
+        for forecast in forecasts:
             if forecast.state != 'cancel':
                 self.raise_user_error('delete_cancel', forecast.rec_name)
-        return super(Forecast, self).delete(ids)
+        super(Forecast, self).delete(forecasts)
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('draft')
-    def draft(self, ids):
+    def draft(cls, forecasts):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('done')
-    def confirm(self, ids):
+    def confirm(cls, forecasts):
         pass
 
+    @classmethod
     @ModelView.button
     @Workflow.transition('cancel')
-    def cancel(self, ids):
+    def cancel(cls, forecasts):
         pass
 
+    @classmethod
     @ModelView.button_action('stock_forecast.wizard_forecast_complete')
-    def complete(self, ids):
+    def complete(cls, forecasts):
         pass
 
-    def create_moves(self, forecast_ids):
+    @staticmethod
+    def create_moves(forecasts):
         'Create stock moves for the forecast ids'
-        line_obj = Pool().get('stock.forecast.line')
-
-        forecasts = self.browse(forecast_ids)
         for forecast in forecasts:
             if forecast.state == 'done':
                 for line in forecast.lines:
-                    line_obj.create_moves(line)
+                    line.create_moves()
 
-    def delete_moves(self, forecast_ids):
+    @staticmethod
+    def delete_moves(forecasts):
         'Delete stock moves for the forecast ids'
-        line_obj = Pool().get('stock.forecast.line')
+        Line = Pool().get('stock.forecast.line')
+        Line.delete_moves([l for f in forecasts for l in f.lines])
 
-        forecasts = self.browse(forecast_ids)
-        for forecast in forecasts:
-            for line in forecast.lines:
-                line_obj.delete_moves(line)
-
-    def copy(self, ids, default=None):
-        line_obj = Pool().get('stock.forecast.line')
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+    @classmethod
+    def copy(cls, forecasts, default=None):
+        Line = Pool().get('stock.forecast.line')
 
         if default is None:
             default = {}
         default = default.copy()
         default['lines'] = None
 
-        new_ids = []
-        for forecast in self.browse(ids):
-            new_id = super(Forecast, self).copy(forecast.id, default=default)
-            line_obj.copy([x.id for x in forecast.lines],
-                    default={
-                        'forecast': new_id,
+        new_forecasts = []
+        for forecast in forecasts:
+            new_forecast, = super(Forecast, cls).copy([forecast],
+                default=default)
+            Line.copy([x for x in forecast.lines],
+                default={
+                    'forecast': new_forecast.id,
                     })
-            new_ids.append(new_id)
-
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-Forecast()
+            new_forecasts.append(new_forecast)
+        return new_forecasts
 
 
 class ForecastLine(ModelSQL, ModelView):
     'Stock Forecast Line'
-    _name = 'stock.forecast.line'
-    _description = __doc__
+    __name__ = 'stock.forecast.line'
     _rec_name = 'product'
-
     product = fields.Many2One('product.product', 'Product', required=True,
         domain=[
             ('type', '=', 'goods'),
@@ -266,7 +258,7 @@
     product_uom_category = fields.Function(
         fields.Many2One('product.uom.category', 'Product Uom Category',
             on_change_with=['product']),
-        'get_product_uom_category')
+        'on_change_with_product_uom_category')
     uom = fields.Many2One('product.uom', 'UOM', required=True,
         domain=[
             If(Bool(Eval('product_uom_category')),
@@ -288,9 +280,10 @@
             digits=(16, Eval('unit_digits', 2)), depends=['unit_digits']),
         'get_quantity_executed')
 
-    def __init__(self):
-        super(ForecastLine, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ForecastLine, cls).__setup__()
+        cls._sql_constraints += [
             ('check_line_qty_pos',
              'CHECK(quantity >= 0.0)', 'Line quantity must be positive!'),
             ('check_line_minimal_qty',
@@ -298,70 +291,54 @@
              'Line quantity must be greater than the minimal quantity!'),
             ('forecast_product_uniq', 'UNIQUE(forecast, product)',
              'Product must be unique by forcast!'),
-        ]
+            ]
 
-    def default_unit_digits(self):
+    @staticmethod
+    def default_unit_digits():
         return 2
 
-    def default_minimal_quantity(self):
+    @staticmethod
+    def default_minimal_quantity():
         return 1.0
 
-    def on_change_product(self, vals):
-        product_obj = Pool().get('product.product')
+    def on_change_product(self):
         res = {}
         res['unit_digits'] = 2
-        if vals.get('product'):
-            product = product_obj.browse(vals['product'])
-            res['uom'] = product.default_uom.id
-            res['uom.rec_name'] = product.default_uom.rec_name
-            res['unit_digits'] = product.default_uom.digits
+        if self.product:
+            res['uom'] = self.product.default_uom.id
+            res['uom.rec_name'] = self.product.default_uom.rec_name
+            res['unit_digits'] = self.product.default_uom.digits
         return res
 
-    def on_change_with_product_uom_category(self, values):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        if values.get('product'):
-            product = product_obj.browse(values['product'])
-            return product.default_uom_category.id
+    def on_change_with_product_uom_category(self, name=None):
+        if self.product:
+            return self.product.default_uom_category.id
 
-    def get_product_uom_category(self, ids, name):
-        categories = {}
-        for line in self.browse(ids):
-            if line.product:
-                categories[line.id] = line.product.default_uom_category.id
-            else:
-                categories[line.id] = None
-        return categories
-
-    def on_change_uom(self, vals):
-        uom_obj = Pool().get('product.uom')
+    def on_change_uom(self):
         res = {}
         res['unit_digits'] = 2
-        if vals.get('uom'):
-            uom = uom_obj.browse(vals['uom'])
-            res['unit_digits'] = uom.digits
+        if self.uom:
+            res['unit_digits'] = self.uom.digits
         return res
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for line in self.browse(ids):
-            res[line.id] = line.product.default_uom.digits
-        return res
+    def get_unit_digits(self, name):
+        return self.product.default_uom.digits
 
-    def get_quantity_executed(self, ids, name):
+    @classmethod
+    def get_quantity_executed(cls, lines, name):
         cursor = Transaction().cursor
-        move_obj = Pool().get('stock.move')
-        location_obj = Pool().get('stock.location')
-        uom_obj = Pool().get('product.uom')
-        forecast_obj = Pool().get('stock.forecast')
-        line_move_obj = Pool().get('stock.forecast.line-stock.move')
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Location = pool.get('stock.location')
+        Uom = pool.get('product.uom')
+        Forecast = pool.get('stock.forecast')
+        LineMove = pool.get('stock.forecast.line-stock.move')
 
-        result = dict((x, 0) for x in ids)
-        lines = self.browse(ids)
+        result = dict((x.id, 0) for x in lines)
         key = lambda line: line.forecast.id
         lines.sort(key=key)
         for forecast_id, lines in itertools.groupby(lines, key):
-            forecast = forecast_obj.browse(forecast_id)
+            forecast = Forecast(forecast_id)
             product2line = dict((line.product.id, line) for line in lines)
             product_ids = product2line.keys()
             for i in range(0, len(product_ids), cursor.IN_MAX):
@@ -369,12 +346,12 @@
                 red_sql, red_ids = reduce_ids('product', sub_ids)
                 cursor.execute('SELECT m.product, '
                         'SUM(m.internal_quantity) AS quantity '
-                    'FROM "' + move_obj._table + '" AS m '
-                        'JOIN "' + location_obj._table + '" AS fl '
+                    'FROM "' + Move._table + '" AS m '
+                        'JOIN "' + Location._table + '" AS fl '
                             'ON m.from_location = fl.id '
-                        'JOIN "' + location_obj._table + '" AS tl '
+                        'JOIN "' + Location._table + '" AS tl '
                             'ON m.to_location = tl.id '
-                        'LEFT JOIN "' + line_move_obj._table + '" AS lm '
+                        'LEFT JOIN "' + LineMove._table + '" AS lm '
                             'ON m.id = lm.move '
                     'WHERE ' + red_sql + ' '
                         'AND fl.left >= %s AND fl.right <= %s '
@@ -390,69 +367,74 @@
                         forecast.from_date, forecast.to_date])
                 for product_id, quantity in cursor.fetchall():
                     line = product2line[product_id]
-                    result[line.id] = uom_obj.compute_qty(
-                        line.product.default_uom, quantity, line.uom)
+                    result[line.id] = Uom.compute_qty(line.product.default_uom,
+                        quantity, line.uom)
         return result
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, lines, default=None):
         if default is None:
             default = {}
         default = default.copy()
         default['moves'] = None
-        return super(ForecastLine, self).copy(ids, default=default)
+        return super(ForecastLine, cls).copy(lines, default=default)
 
-    def create_moves(self, line):
+    def create_moves(self):
         'Create stock moves for the forecast line'
-        move_obj = Pool().get('stock.move')
-        uom_obj = Pool().get('product.uom')
-        date_obj = Pool().get('ir.date')
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Uom = pool.get('product.uom')
+        Date = pool.get('ir.date')
 
-        assert not line.moves
+        assert not self.moves
 
-        today = date_obj.today()
-        from_date = line.forecast.from_date
+        today = Date.today()
+        from_date = self.forecast.from_date
         if from_date < today:
             from_date = today
-        to_date = line.forecast.to_date
+        to_date = self.forecast.to_date
         if to_date < today:
             return
 
         delta = to_date - from_date
         delta = delta.days + 1
-        nb_packet = int((line.quantity - line.quantity_executed)
-            / line.minimal_quantity)
+        nb_packet = int((self.quantity - self.quantity_executed)
+            / self.minimal_quantity)
         distribution = self.distribute(delta, nb_packet)
         unit_price = None
-        if line.forecast.destination.type == 'customer':
-            unit_price = line.product.list_price
-            unit_price = uom_obj.compute_price(line.product.default_uom,
-                    unit_price, line.uom)
+        if self.forecast.destination.type == 'customer':
+            unit_price = self.product.list_price
+            unit_price = Uom.compute_price(self.product.default_uom,
+                unit_price, self.uom)
 
         moves = []
         for day, qty in distribution.iteritems():
             if qty == 0.0:
                 continue
-            mid = move_obj.create({
-                'from_location': line.forecast.warehouse.storage_location.id,
-                'to_location': line.forecast.destination.id,
-                'product': line.product.id,
-                'uom': line.uom.id,
-                'quantity': qty * line.minimal_quantity,
-                'planned_date': (line.forecast.from_date
+            move = Move.create({
+                    'from_location': \
+                        self.forecast.warehouse.storage_location.id,
+                    'to_location': self.forecast.destination.id,
+                    'product': self.product.id,
+                    'uom': self.uom.id,
+                    'quantity': qty * self.minimal_quantity,
+                    'planned_date': (self.forecast.from_date
                         + datetime.timedelta(day)),
-                'company': line.forecast.company.id,
-                'currency': line.forecast.company.currency.id,
-                'unit_price': unit_price,
-                })
-            moves.append(mid)
-        self.write(line.id, {'moves': [('set', moves)]})
+                    'company': self.forecast.company.id,
+                    'currency': self.forecast.company.currency.id,
+                    'unit_price': unit_price,
+                    })
+            moves.append(move)
+        self.write([self], {'moves': [('set', [m.id for m in moves])]})
 
-    def delete_moves(self, line):
+    @classmethod
+    def delete_moves(cls, lines):
         'Delete stock moves of the forecast line'
-        move_obj = Pool().get('stock.move')
-        move_obj.delete([m.id for m in line.moves])
+        Move = Pool().get('stock.move')
+        Move.delete([m for l in lines for m in l.moves])
 
-    def distribute(self, delta, qty):
+    @staticmethod
+    def distribute(delta, qty):
         'Distribute qty over delta'
         range_delta = range(delta)
         a = {}.fromkeys(range_delta, 0)
@@ -479,44 +461,33 @@
                 qty = 0
         return a
 
-ForecastLine()
-
 
 class ForecastLineMove(ModelSQL):
     'ForecastLine - Move'
-    _name = 'stock.forecast.line-stock.move'
+    __name__ = 'stock.forecast.line-stock.move'
     _table = 'forecast_line_stock_move_rel'
-    _description = __doc__
     line = fields.Many2One('stock.forecast.line', 'Forecast Line',
             ondelete='CASCADE', select=True, required=True)
     move = fields.Many2One('stock.move', 'Move', ondelete='CASCADE',
             select=True, required=True)
 
-ForecastLineMove()
-
 
 class ForecastCompleteAsk(ModelView):
     'Complete Forecast'
-    _name = 'stock.forecast.complete.ask'
-    _description = __doc__
+    __name__ = 'stock.forecast.complete.ask'
     from_date = fields.Date('From Date', required=True)
     to_date = fields.Date('To Date', required=True)
 
-ForecastCompleteAsk()
-
 
 class ForecastCompleteChoose(ModelView):
     'Complete Forecast'
-    _name = 'stock.forecast.complete.choose'
-    _description = __doc__
+    __name__ = 'stock.forecast.complete.choose'
     products = fields.Many2Many('product.product', None, None, 'Products')
 
-ForecastCompleteChoose()
-
 
 class ForecastComplete(Wizard):
     'Complete Forecast'
-    _name = 'stock.forecast.complete'
+    __name__ = 'stock.forecast.complete'
     start_state = 'ask'
     ask = StateView('stock.forecast.complete.ask',
         'stock_forecast.forecast_comlete_ask_view_form', [
@@ -532,72 +503,74 @@
             ])
     complete = StateTransition()
 
-    def __init__(self):
-        super(ForecastComplete, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(ForecastComplete, cls).__setup__()
+        cls._error_messages.update({
             'from_to_date': '"From Date" should be smaller than "To Date"!',
             })
 
-    def default_ask(self, session, fields):
+    def default_ask(self, fields):
         """
         Forecast dates shifted by one year.
         """
-        forecast_obj = Pool().get('stock.forecast')
-        forecast = forecast_obj.browse(Transaction().context['active_id'])
+        Forecast = Pool().get('stock.forecast')
+        forecast = Forecast(Transaction().context['active_id'])
 
         res = {}
         for field in ("to_date", "from_date"):
             res[field] = forecast[field] - relativedelta(years=1)
         return res
 
-    def _get_product_quantity(self, session):
-        forecast_obj = Pool().get('stock.forecast')
-        product_obj = Pool().get('product.product')
-        forecast = forecast_obj.browse(Transaction().context['active_id'])
-        if session.ask.from_date > session.ask.to_date:
+    def _get_product_quantity(self):
+        pool = Pool()
+        Forecast = pool.get('stock.forecast')
+        Product = pool.get('product.product')
+        forecast = Forecast(Transaction().context['active_id'])
+        if self.ask.from_date > self.ask.to_date:
             self.raise_user_error('from_to_date')
 
         with Transaction().set_context(
                 stock_destination=[forecast.destination.id],
-                stock_date_start=session.ask.from_date,
-                stock_date_end=session.ask.to_date):
-            return product_obj.products_by_location([forecast.warehouse.id],
+                stock_date_start=self.ask.from_date,
+                stock_date_end=self.ask.to_date):
+            return Product.products_by_location([forecast.warehouse.id],
                     with_childs=True, skip_zero=False)
 
-    def default_choose(self, session, fields):
+    def default_choose(self, fields):
         """
         Collect products for which there is an outgoing stream between
         the given location and the destination.
         """
-        if session.choose.products:
-            return {'products': [x.id for x in session.choose.products]}
-        pbl = self._get_product_quantity(session)
+        if self.choose.products:
+            return {'products': [x.id for x in self.choose.products]}
+        pbl = self._get_product_quantity()
         products = []
         for (_, product), qty in pbl.iteritems():
             if qty < 0:
                 products.append(product)
         return {'products': products}
 
-    def transition_complete(self, session):
+    def transition_complete(self):
         pool = Pool()
-        forecast_line_obj = pool.get('stock.forecast.line')
-        product_obj = pool.get('product.product')
+        ForecastLine = pool.get('stock.forecast.line')
+        Product = pool.get('product.product')
 
         prod2line = {}
-        forecast_line_ids = forecast_line_obj.search([
+        forecast_lines = ForecastLine.search([
                 ('forecast', '=', Transaction().context['active_id']),
                 ])
-        for forecast_line in forecast_line_obj.browse(forecast_line_ids):
-            prod2line[forecast_line.product.id] = forecast_line.id
+        for forecast_line in forecast_lines:
+            prod2line[forecast_line.product.id] = forecast_line
 
-        pbl = self._get_product_quantity(session)
+        pbl = self._get_product_quantity()
         product_ids = [x[1] for x in pbl]
         prod2uom = {}
-        for product in product_obj.browse(product_ids):
+        for product in Product.browse(product_ids):
             prod2uom[product.id] = product.default_uom.id
 
-        if session.choose.products:
-            products = [x.id for x in session.choose.products]
+        if self.choose.products:
+            products = [x.id for x in self.choose.products]
         else:
             products = None
 
@@ -608,7 +581,7 @@
             if -qty <= 0:
                 continue
             if product in prod2line:
-                forecast_line_obj.write(prod2line[product], {
+                ForecastLine.write([prod2line[product]], {
                         'product': product,
                         'quantity': -qty,
                         'uom': prod2uom[product],
@@ -616,7 +589,7 @@
                         'minimal_quantity': min(1, -qty),
                         })
             else:
-                forecast_line_obj.create({
+                ForecastLine.create({
                         'product': product,
                         'quantity': -qty,
                         'uom': prod2uom[product],
@@ -624,5 +597,3 @@
                         'minimal_quantity': min(1, -qty),
                         })
         return 'end'
-
-ForecastComplete()
diff -r 4d2ecbb9bb2a trytond/modules/stock_forecast/tests/test_stock_forecast.py
--- a/trytond/modules/stock_forecast/tests/test_stock_forecast.py	Mon Sep 10 15:28:40 2012 +0200
+++ b/trytond/modules/stock_forecast/tests/test_stock_forecast.py	Mon Sep 24 20:36:43 2012 +0200
@@ -66,78 +66,73 @@
         Test create_moves.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            category_id = self.category.create({
+            category = self.category.create({
                     'name': 'Test create_moves',
                     })
-            unit_id, = self.uom.search([('name', '=', 'Unit')])
-            product_id = self.product.create({
+            unit, = self.uom.search([('name', '=', 'Unit')])
+            product = self.product.create({
                     'name': 'Test create_moves',
                     'type': 'goods',
-                    'category': category_id,
+                    'category': category.id,
                     'cost_price_method': 'fixed',
-                    'default_uom': unit_id,
+                    'default_uom': unit.id,
                     'list_price': Decimal('1'),
                     'cost_price': Decimal(0),
                     })
-            customer_id, = self.location.search([('code', '=', 'CUS')])
-            warehouse_id, = self.location.search([('code', '=', 'WH')])
-            storage_id, = self.location.search([('code', '=', 'STO')])
-            company_id, = self.company.search([('name', '=', 'B2CK')])
-            self.user.write(USER, {
-                    'main_company': company_id,
-                    'company': company_id,
+            customer, = self.location.search([('code', '=', 'CUS')])
+            warehouse, = self.location.search([('code', '=', 'WH')])
+            storage, = self.location.search([('code', '=', 'STO')])
+            company, = self.company.search([('name', '=', 'B2CK')])
+            self.user.write([self.user(USER)], {
+                    'main_company': company.id,
+                    'company': company.id,
                     })
 
             today = datetime.date.today()
 
-            forecast_id = self.forecast.create({
-                    'warehouse': warehouse_id,
-                    'destination': customer_id,
+            forecast = self.forecast.create({
+                    'warehouse': warehouse.id,
+                    'destination': customer.id,
                     'from_date': today + relativedelta(months=1, day=1),
                     'to_date': today + relativedelta(months=1, day=20),
-                    'company': company_id,
+                    'company': company.id,
                     'lines': [
                         ('create', {
-                                'product': product_id,
+                                'product': product.id,
                                 'quantity': 10,
-                                'uom': unit_id,
+                                'uom': unit.id,
                                 'minimal_quantity': 2,
                                 },
                             ),
                         ],
                     })
-            self.forecast.confirm([forecast_id])
+            self.forecast.confirm([forecast])
 
-            self.forecast.create_moves([forecast_id])
-            forecast = self.forecast.browse(forecast_id)
+            self.forecast.create_moves([forecast])
             line, = forecast.lines
             self.assertEqual(line.quantity_executed, 0)
             self.assertEqual(len(line.moves), 5)
             self.assertEqual(sum(move.quantity for move in line.moves), 10)
 
-            self.forecast.delete_moves([forecast_id])
-            forecast = self.forecast.browse(forecast_id)
+            self.forecast.delete_moves([forecast])
             line, = forecast.lines
             self.assertEqual(len(line.moves), 0)
 
-            company = self.company.browse(company_id)
             self.move.create({
-                    'from_location': storage_id,
-                    'to_location': customer_id,
-                    'product': product_id,
-                    'uom': unit_id,
+                    'from_location': storage.id,
+                    'to_location': customer.id,
+                    'product': product.id,
+                    'uom': unit.id,
                     'quantity': 2,
                     'planned_date': today + relativedelta(months=1, day=5),
-                    'company': company_id,
+                    'company': company.id,
                     'currency': company.currency.id,
                     'unit_price': Decimal('1'),
                     })
-            forecast = self.forecast.browse(forecast_id)
             line, = forecast.lines
             self.assertEqual(line.quantity_executed, 2)
 
-            self.forecast.create_moves([forecast_id])
-            forecast = self.forecast.browse(forecast_id)
+            self.forecast.create_moves([forecast])
             line, = forecast.lines
             self.assertEqual(line.quantity_executed, 2)
             self.assertEqual(len(line.moves), 4)
diff -r 23e95b2814bb trytond/modules/stock_inventory_location/__init__.py
--- a/trytond/modules/stock_inventory_location/__init__.py	Fri Jun 08 19:34:29 2012 +0200
+++ b/trytond/modules/stock_inventory_location/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,4 +1,14 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of this
 #repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .inventory import *
+
+
+def register():
+    Pool.register(
+        CreateInventoriesStart,
+        module='stock_inventory_location', type_='model')
+    Pool.register(
+        CreateInventories,
+        module='stock_inventory_location', type_='wizard')
diff -r 23e95b2814bb trytond/modules/stock_inventory_location/inventory.py
--- a/trytond/modules/stock_inventory_location/inventory.py	Fri Jun 08 19:34:29 2012 +0200
+++ b/trytond/modules/stock_inventory_location/inventory.py	Mon Sep 24 20:36:43 2012 +0200
@@ -5,12 +5,12 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['CreateInventoriesStart', 'CreateInventories']
+
 
 class CreateInventoriesStart(ModelView):
     'Create Inventories'
-    _name = 'stock.inventory.create.start'
-    _description = __doc__
-
+    __name__ = 'stock.inventory.create.start'
     date = fields.Date('Date', required=True)
     lost_found = fields.Many2One(
         'stock.location', 'Lost and Found', required=True,
@@ -20,22 +20,21 @@
     locations = fields.Many2Many('stock.location', None, None,
             'Locations', required=True, domain=[('type', '=', 'storage')])
 
-    def default_lost_found(self):
-        location_obj = Pool().get('stock.location')
-        location_ids = location_obj.search(self.lost_found.domain)
-        if len(location_ids) == 1:
-            return location_ids[0]
+    @classmethod
+    def default_lost_found(cls):
+        Location = Pool().get('stock.location')
+        locations = Location.search(cls.lost_found.domain)
+        if len(locations) == 1:
+            return locations[0].id
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-CreateInventoriesStart()
-
 
 class CreateInventories(Wizard):
     'Create Inventories'
-    _name = 'stock.inventory.create'
-
+    __name__ = 'stock.inventory.create'
     start = StateView('stock.inventory.create.start',
         'stock_inventory_location.inventory_create_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -43,24 +42,19 @@
             ])
     create_ = StateAction('stock.act_inventory_form')
 
-    def do_create_(self, session, action):
-        pool = Pool()
-        inventory_obj = pool.get('stock.inventory')
+    def do_create_(self, action):
+        Inventory = Pool().get('stock.inventory')
 
-        inventory_ids = []
-        location_ids = [x.id for x in session.start.locations]
-
-        for location_id in location_ids:
-            inventory_ids.append(inventory_obj.create({
-                        'location': location_id,
-                        'date': session.start.date,
-                        'lost_found': session.start.lost_found.id,
-                        'company': session.start.company.id,
+        inventories = []
+        for location in self.start.locations:
+            inventories.append(Inventory.create({
+                        'location': location.id,
+                        'date': self.start.date,
+                        'lost_found': self.start.lost_found.id,
+                        'company': self.start.company.id,
                         }))
 
-        inventory_obj.complete_lines(inventory_ids)
+        Inventory.complete_lines(inventories)
 
-        data = {'res_id': inventory_ids}
+        data = {'res_id': [i.id for i in inventories]}
         return action, data
-
-CreateInventories()
diff -r c7283a249034 trytond/modules/stock_location_sequence/__init__.py
--- a/trytond/modules/stock_location_sequence/__init__.py	Fri Jun 08 19:34:30 2012 +0200
+++ b/trytond/modules/stock_location_sequence/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,4 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of this
 #repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .stock import *
+
+
+def register():
+    Pool.register(
+        Location,
+        module='stock_location_sequence', type_='model')
diff -r c7283a249034 trytond/modules/stock_location_sequence/stock.py
--- a/trytond/modules/stock_location_sequence/stock.py	Fri Jun 08 19:34:30 2012 +0200
+++ b/trytond/modules/stock_location_sequence/stock.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,14 +1,18 @@
 #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 ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Not, Eval, Bool
 from trytond.transaction import Transaction
 from trytond.backend import TableHandler
+from trytond.pool import PoolMeta
 
+__all__ = ['Location']
+__metaclass__ = PoolMeta
 
-class Location(ModelSQL, ModelView):
+
+class Location:
     "Stock Location"
-    _name = 'stock.location'
+    __name__ = 'stock.location'
     sequence = fields.Integer('Sequence',
         order_field='(%(table)s.sequence IS NULL) %(order)s, '
         '%(table)s.sequence %(order)s',
@@ -17,17 +21,17 @@
             },
         depends=['active'])
 
-    def __init__(self):
-        super(Location, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(Location, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(Location, self).init(module_name)
+        super(Location, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
-
-Location()
diff -r e03001f00e60 trytond/modules/stock_lot/__init__.py
--- a/trytond/modules/stock_lot/__init__.py	Tue Jul 31 10:46:25 2012 +0200
+++ b/trytond/modules/stock_lot/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,5 +1,20 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of this
 #repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .stock import *
 from .product import *
+
+
+def register():
+    Pool.register(
+        Lot,
+        LotType,
+        Move,
+        ShipmentIn,
+        ShipmentOut,
+        ShipmentOutReturn,
+        Template,
+        Product,
+        TemplateLotType,
+        module='stock_lot', type_='model')
diff -r e03001f00e60 trytond/modules/stock_lot/product.py
--- a/trytond/modules/stock_lot/product.py	Tue Jul 31 10:46:25 2012 +0200
+++ b/trytond/modules/stock_lot/product.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,11 +1,15 @@
 #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 Model, ModelSQL, fields
+from trytond.model import ModelSQL, fields
 from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 
+__all__ = ['Template', 'Product', 'TemplateLotType']
+__metaclass__ = PoolMeta
 
-class Template(Model):
-    _name = 'product.template'
+
+class Template:
+    __name__ = 'product.template'
 
     # TODO to replace with a Multiple Selection
     lot_required = fields.Many2Many('product.template-stock.lot.type',
@@ -16,28 +20,22 @@
             },
         depends=['type'])
 
-Template()
 
+class Product:
+    __name__ = 'product.product'
 
-class Product(Model):
-    _name = 'product.product'
-
-    def lot_is_required(self, product, from_, to):
+    def lot_is_required(self, from_, to):
         'Is product lot required for move with "from_" and "to" location ?'
-        lot_required = [t.code for t in product.lot_required]
+        lot_required = [t.code for t in self.lot_required]
         for location in (from_, to):
             if location.type in lot_required:
                 return True
 
-Product()
-
 
 class TemplateLotType(ModelSQL):
     'Template - Stock Lot Type'
-    _name = 'product.template-stock.lot.type'
+    __name__ = 'product.template-stock.lot.type'
 
     template = fields.Many2One('product.template', 'Template', required=True,
         select=True)
     type = fields.Many2One('stock.lot.type', 'Type', required=True)
-
-TemplateLotType()
diff -r e03001f00e60 trytond/modules/stock_lot/stock.py
--- a/trytond/modules/stock_lot/stock.py	Tue Jul 31 10:46:25 2012 +0200
+++ b/trytond/modules/stock_lot/stock.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,36 +1,31 @@
 #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 Model, ModelView, ModelSQL, Workflow, fields
+from trytond.model import ModelView, ModelSQL, Workflow, fields
 from trytond.pyson import Eval
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
+
+__all__ = ['Lot', 'LotType', 'Move', 'ShipmentIn', 'ShipmentOut',
+    'ShipmentOutReturn']
+__metaclass__ = PoolMeta
 
 
 class Lot(ModelSQL, ModelView):
     "Stock Lot"
-    _name = 'stock.lot'
-    _description = __doc__
+    __name__ = 'stock.lot'
     _rec_name = 'number'
-
     number = fields.Char('Number', required=True, select=True)
     product = fields.Many2One('product.product', 'Product', required=True)
 
-Lot()
-
 
 class LotType(ModelSQL, ModelView):
     "Stock Lot Type"
-    _name = 'stock.lot.type'
-    _description = __doc__
-
+    __name__ = 'stock.lot.type'
     name = fields.Char('Name', required=True, translate=True)
     code = fields.Char('Code', required=True)
 
-LotType()
 
-
-class Move(Model):
-    _name = 'stock.move'
-
+class Move:
+    __name__ = 'stock.move'
     lot = fields.Many2One('stock.lot', 'Lot',
         domain=[
             ('product', '=', Eval('product')),
@@ -40,66 +35,61 @@
             },
         depends=['state', 'product'])
 
-    def __init__(self):
-        super(Move, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Move, cls).__setup__()
+        cls._error_messages.update({
                 'lot_required': 'Lot is required for move of product "%s"!',
                 })
 
-    def check_lot(self, ids):
+    @classmethod
+    def check_lot(cls, moves):
         "Check if lot is required"
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        for move in self.browse(ids):
+        for move in moves:
             if (move.state == 'done'
                     and not move.lot
-                    and product_obj.lot_is_required(move.product,
+                    and move.product.lot_is_required(
                         move.from_location, move.to_location)):
-                self.raise_user_error('lot_required', (move.product.rec_name,))
+                cls.raise_user_error('lot_required', (move.product.rec_name,))
 
-    def create(self, values):
-        new_id = super(Move, self).create(values)
-        self.check_lot([new_id])
-        return new_id
+    @classmethod
+    def create(cls, values):
+        move = super(Move, cls).create(values)
+        cls.check_lot([move])
+        return move
 
-    def write(self, ids, values):
-        result = super(Move, self).write(ids, values)
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        self.check_lot(ids)
-        return result
+    @classmethod
+    def write(cls, moves, values):
+        super(Move, cls).write(moves, values)
+        cls.check_lot(moves)
 
-Move()
 
+class ShipmentIn:
+    __name__ = 'stock.shipment.in'
 
-class ShipmentIn(Model):
-    _name = 'stock.shipment.in'
-
-    def _get_inventory_moves(self, incoming_move):
-        result = super(ShipmentIn, self)._get_inventory_moves(incoming_move)
-        if result:
+    @classmethod
+    def _get_inventory_moves(cls, incoming_move):
+        result = super(ShipmentIn, cls)._get_inventory_moves(incoming_move)
+        if result and incoming_move.lot:
             result['lot'] = incoming_move.lot.id
         return result
 
-ShipmentIn()
 
+class ShipmentOut:
+    __name__ = 'stock.shipment.out'
 
-class ShipmentOut(Model):
-    _name = 'stock.shipment.out'
-
+    @classmethod
     @ModelView.button
     @Workflow.transition('packed')
-    def pack(self, ids):
+    def pack(cls, shipments):
         pool = Pool()
-        uom_obj = pool.get('product.uom')
-        move_obj = pool.get('stock.move')
+        Uom = pool.get('product.uom')
+        Move = pool.get('stock.move')
 
-        super(ShipmentOut, self).pack(ids)
-
-        shipments = self.browse(ids)
+        super(ShipmentOut, cls).pack(shipments)
 
         # Unassign move to allow update
-        move_obj.write([m.id for s in shipments for m in s.outgoing_moves
+        Move.write([m for s in shipments for m in s.outgoing_moves
                 if m.state not in ('done', 'cancel')], {
                 'state': 'draft',
                 })
@@ -112,47 +102,40 @@
             for move in shipment.inventory_moves:
                 if not move.lot:
                     continue
-                quantity = uom_obj.compute_qty(move.uom, move.quantity,
+                quantity = Uom.compute_qty(move.uom, move.quantity,
                     move.product.default_uom, round=False)
                 outgoing_moves = outgoing_by_product[move.product.id]
                 while outgoing_moves and quantity > 0:
                     out_move = outgoing_moves.pop()
-                    out_quantity = uom_obj.compute_qty(out_move.uom,
+                    out_quantity = Uom.compute_qty(out_move.uom,
                         out_move.quantity, out_move.product.default_uom,
                         round=False)
                     if quantity < out_quantity:
-                        outgoing_moves.append(move_obj.browse(
-                                move_obj.copy(out_move.id, default={
-                                        'quantity': out_quantity - quantity,
-                                        })))
-                        move_obj.write(out_move.id, {
+                        outgoing_moves.extend(Move.copy([out_move], default={
+                                    'quantity': out_quantity - quantity,
+                                    }))
+                        Move.write([out_move], {
                                 'quantity': quantity,
                                 })
-                    move_obj.write(out_move.id, {
+                    Move.write([out_move], {
                             'lot': move.lot.id,
                             })
                     quantity -= out_quantity
                 assert quantity <= 0
 
-        # Reset function field cache
-        shipments = self.browse(ids)
-
-        move_obj.write([m.id for s in shipments for m in s.outgoing_moves
+        Move.write([m for s in shipments for m in s.outgoing_moves
                 if m.state != 'cancel'], {
                 'state': 'assigned',
                 })
 
-ShipmentOut()
 
+class ShipmentOutReturn:
+    __name__ = 'stock.shipment.out.return'
 
-class ShipmentOutReturn(Model):
-    _name = 'stock.shipment.out.return'
-
-    def _get_inventory_moves(self, incoming_move):
+    @classmethod
+    def _get_inventory_moves(cls, incoming_move):
         result = super(ShipmentOutReturn,
-            self)._get_inventory_moves(incoming_move)
-        if result:
+            cls)._get_inventory_moves(incoming_move)
+        if result and incoming_move.lot:
             result['lot'] = incoming_move.lot.id
         return result
-
-ShipmentOutReturn()
diff -r f78800bc9893 trytond/modules/stock_product_location/__init__.py
--- a/trytond/modules/stock_product_location/__init__.py	Fri Jun 08 19:34:31 2012 +0200
+++ b/trytond/modules/stock_product_location/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,5 +1,14 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .product import *
 from .location import *
+
+
+def register():
+    Pool.register(
+        Product,
+        ProductLocation,
+        ShipmentIn,
+        module='stock_product_location', type_='model')
diff -r f78800bc9893 trytond/modules/stock_product_location/location.py
--- a/trytond/modules/stock_product_location/location.py	Fri Jun 08 19:34:31 2012 +0200
+++ b/trytond/modules/stock_product_location/location.py	Mon Sep 24 20:36:43 2012 +0200
@@ -4,16 +4,18 @@
 from trytond.pyson import If, Eval, Bool
 from trytond.transaction import Transaction
 from trytond.backend import TableHandler
+from trytond.pool import PoolMeta
+
+__all__ = ['ProductLocation', 'ShipmentIn']
+__metaclass__ = PoolMeta
 
 
 class ProductLocation(ModelSQL, ModelView):
     '''
-    Product Location defines the default storage location
-    by warehouse for a product
+    Product Location
+    It defines the default storage location by warehouse for a product.
     '''
-    _name = 'stock.product.location'
-    _description = 'Product Location'
-
+    __name__ = 'stock.product.location'
     product = fields.Many2One('product.product', 'Product', required=True,
             select=True)
     warehouse = fields.Many2One('stock.location', 'Warehouse', required=True,
@@ -28,32 +30,31 @@
         order_field='(%(table)s.sequence IS NULL) %(order)s, '
         '%(table)s.sequence %(order)s')
 
-    def __init__(self):
-        super(ProductLocation, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ProductLocation, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
-        super(ProductLocation, self).init(module_name)
+        super(ProductLocation, cls).__register__(module_name)
 
         # Migration from 2.4: drop required on sequence
         table.not_null_action('sequence', action='remove')
 
-ProductLocation()
 
+class ShipmentIn:
+    __name__ = 'stock.shipment.in'
 
-class ShipmentIn(ModelSQL, ModelView):
-    _name = 'stock.shipment.in'
-
-    def _get_inventory_moves(self, incoming_move):
-        res = super(ShipmentIn, self)._get_inventory_moves(incoming_move)
+    @classmethod
+    def _get_inventory_moves(cls, incoming_move):
+        res = super(ShipmentIn, cls)._get_inventory_moves(incoming_move)
         for product_location in incoming_move.product.locations:
             if product_location.warehouse.id != \
                     incoming_move.shipment_in.warehouse.id:
                 continue
             res['to_location'] = product_location.location.id
         return res
-
-ShipmentIn()
diff -r f78800bc9893 trytond/modules/stock_product_location/product.py
--- a/trytond/modules/stock_product_location/product.py	Fri Jun 08 19:34:31 2012 +0200
+++ b/trytond/modules/stock_product_location/product.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,15 +1,16 @@
 #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 ModelView, ModelSQL, fields
+from trytond.model import fields
 from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 
+__all__ = ['Product']
+__metaclass__ = PoolMeta
 
-class Product(ModelSQL, ModelView):
-    _name = 'product.product'
 
+class Product:
+    __name__ = 'product.product'
     locations = fields.One2Many('stock.product.location', 'product',
         'Default Locations', states={
             'invisible': ~Eval('type').in_(['goods', 'assets']),
             })
-
-Product()
diff -r f2000f2db021 trytond/modules/stock_split/__init__.py
--- a/trytond/modules/stock_split/__init__.py	Mon Sep 10 15:28:47 2012 +0200
+++ b/trytond/modules/stock_split/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,4 +1,15 @@
 #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.pool import Pool
 from .stock import *
+
+
+def register():
+    Pool.register(
+        Move,
+        SplitMoveStart,
+        module='stock_split', type_='model')
+    Pool.register(
+        SplitMove,
+        module='stock_split', type_='wizard')
diff -r f2000f2db021 trytond/modules/stock_split/stock.py
--- a/trytond/modules/stock_split/stock.py	Mon Sep 10 15:28:47 2012 +0200
+++ b/trytond/modules/stock_split/stock.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,46 +1,51 @@
 #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 Model, ModelView, fields
+from trytond.model import ModelView, fields
 from trytond.wizard import Wizard, StateView, StateTransition, Button
 from trytond.pyson import Eval
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 
+__all__ = ['Move', 'SplitMoveStart', 'SplitMove']
+__metaclass__ = PoolMeta
 
-class Move(Model):
-    _name = 'stock.move'
 
-    def __init__(self):
-        super(Move, self).__init__()
-        self._buttons.update({
+class Move:
+    __name__ = 'stock.move'
+
+    @classmethod
+    def __setup__(cls):
+        super(Move, cls).__setup__()
+        cls._buttons.update({
                 'split_wizard': {
                     'readonly': ~Eval('state').in_(['draft', 'assigned']),
                     },
                 })
 
+    @classmethod
     @ModelView.button_action('stock_split.wizard_split_move')
-    def split_wizard(self, ids):
+    def split_wizard(cls, moves):
         pass
 
-    def split(self, move, quantity, uom, count=None):
+    def split(self, quantity, uom, count=None):
         """
         Split the move into moves of quantity.
         If count is not defined, the move will be split until the remainder is
         less than quantity.
-        Return the split move ids
+        Return the split moves
         """
         pool = Pool()
-        uom_obj = pool.get('product.uom')
+        Uom = pool.get('product.uom')
 
-        moves = [move.id]
-        remainder = uom_obj.compute_qty(move.uom, move.quantity, uom)
+        moves = [self]
+        remainder = Uom.compute_qty(self.uom, self.quantity, uom)
         if remainder <= quantity:
             return moves
-        state = move.state
-        self.write(move.id, {
+        state = self.state
+        self.write([self], {
                 'state': 'draft',
                 })
-        self.write(move.id, {
+        self.write([self], {
                 'quantity': quantity,
                 'uom': uom.id,
                 'state': state,
@@ -50,7 +55,7 @@
             count -= 1
         while (remainder > quantity
                 and (count or count is None)):
-            moves.append(self.copy(move.id, {
+            moves.extend(self.copy([self], {
                         'quantity': quantity,
                         'uom': uom.id,
                         'state': state,
@@ -60,21 +65,17 @@
                 count -= 1
         assert remainder >= 0
         if remainder:
-            moves.append(self.copy(move.id, {
+            moves.extend(self.copy([self], {
                         'quantity': remainder,
                         'uom': uom.id,
                         'state': state,
                         }))
         return moves
 
-Move()
-
 
 class SplitMoveStart(ModelView):
     'Split Move'
-    _name = 'stock.move.split.start'
-    _description = __doc__
-
+    __name__ = 'stock.move.split.start'
     count = fields.Integer('Counts', help='The limit number of moves')
     quantity = fields.Float('Quantity', required=True,
         digits=(16, Eval('unit_digits', 2)),
@@ -89,21 +90,15 @@
     uom_category = fields.Many2One('product.uom.category', 'Uom Category',
         readonly=True)
 
-    def on_change_with_unit_digits(self, values):
-        pool = Pool()
-        uom_obj = pool.get('product.uom')
-        if values.get('uom'):
-            uom = uom_obj.browse(values['uom'])
-            return uom.digits
+    def on_change_with_unit_digits(self):
+        if self.uom:
+            return self.uom.digits
         return 2
 
-SplitMoveStart()
-
 
 class SplitMove(Wizard):
     'Split Move'
-    _name = 'stock.move.split'
-
+    __name__ = 'stock.move.split'
     start = StateView('stock.move.split.start',
         'stock_split.split_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -111,22 +106,19 @@
             ])
     split = StateTransition()
 
-    def default_start(self, session, fields):
+    def default_start(self, fields):
         pool = Pool()
-        move_obj = pool.get('stock.move')
+        Move = pool.get('stock.move')
         default = {}
-        move = move_obj.browse(Transaction().context['active_id'])
+        move = Move(Transaction().context['active_id'])
         default['uom'] = move.uom.id
         default['unit_digits'] = move.unit_digits
         default['uom_category'] = move.uom.category.id
         return default
 
-    def transition_split(self, session):
+    def transition_split(self):
         pool = Pool()
-        move_obj = pool.get('stock.move')
-        move = move_obj.browse(Transaction().context['active_id'])
-        move_obj.split(move, session.start.quantity, session.start.uom,
-            session.start.count)
+        Move = pool.get('stock.move')
+        move = Move(Transaction().context['active_id'])
+        move.split(self.start.quantity, self.start.uom, self.start.count)
         return 'end'
-
-SplitMove()
diff -r f2000f2db021 trytond/modules/stock_split/tests/test_stock_split.py
--- a/trytond/modules/stock_split/tests/test_stock_split.py	Mon Sep 10 15:28:47 2012 +0200
+++ b/trytond/modules/stock_split/tests/test_stock_split.py	Mon Sep 24 20:36:43 2012 +0200
@@ -49,79 +49,72 @@
         Test split.
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            unit_id, = self.uom.search([('name', '=', 'Unit')])
-            unit = self.uom.browse(unit_id)
-            product_id = self.product.create({
+            unit, = self.uom.search([('name', '=', 'Unit')])
+            product = self.product.create({
                     'name': 'Test Split',
                     'type': 'goods',
                     'cost_price_method': 'fixed',
-                    'default_uom': unit_id,
+                    'default_uom': unit.id,
                     'list_price': Decimal(0),
                     'cost_price': Decimal(0),
                     })
-            input_id, = self.location.search([('code', '=', 'IN')])
-            storage_id, = self.location.search([('code', '=', 'STO')])
-            company_id, = self.company.search([('name', '=', 'B2CK')])
-            self.user.write(USER, {
-                'main_company': company_id,
-                'company': company_id,
-                })
+            input_, = self.location.search([('code', '=', 'IN')])
+            storage, = self.location.search([('code', '=', 'STO')])
+            company, = self.company.search([('name', '=', 'B2CK')])
+            self.user.write([self.user(USER)], {
+                    'main_company': company.id,
+                    'company': company.id,
+                    })
 
             def create_move(quantity):
-                move_id = self.move.create({
-                        'product': product_id,
-                        'uom': unit_id,
+                move = self.move.create({
+                        'product': product.id,
+                        'uom': unit.id,
                         'quantity': quantity,
-                        'from_location': input_id,
-                        'to_location': storage_id,
-                        'company': company_id,
+                        'from_location': input_.id,
+                        'to_location': storage.id,
+                        'company': company.id,
                         })
-                return self.move.browse(move_id)
+                return move
 
             move = create_move(10)
-            move_ids = self.move.split(move, 5, unit)
-            self.assertEqual(len(move_ids), 2)
-            self.assertEqual([m.quantity for m in self.move.browse(move_ids)],
-                [5, 5])
+            moves = move.split(5, unit)
+            self.assertEqual(len(moves), 2)
+            self.assertEqual([m.quantity for m in moves], [5, 5])
 
             move = create_move(13)
-            move_ids = self.move.split(move, 5, unit)
-            self.assertEqual(len(move_ids), 3)
-            self.assertEqual([m.quantity for m in self.move.browse(move_ids)],
-                [5, 5, 3])
+            moves = move.split(5, unit)
+            self.assertEqual(len(moves), 3)
+            self.assertEqual([m.quantity for m in moves], [5, 5, 3])
 
             move = create_move(7)
-            move_ids = self.move.split(move, 8, unit)
-            self.assertEqual(move_ids, [move.id])
+            moves = move.split(8, unit)
+            self.assertEqual(moves, [move])
             self.assertEqual(move.quantity, 7)
 
             move = create_move(20)
-            move_ids = self.move.split(move, 5, unit, count=2)
-            self.assertEqual(len(move_ids), 3)
-            self.assertEqual([m.quantity for m in self.move.browse(move_ids)],
-                [5, 5, 10])
+            moves = move.split(5, unit, count=2)
+            self.assertEqual(len(moves), 3)
+            self.assertEqual([m.quantity for m in moves], [5, 5, 10])
 
             move = create_move(20)
-            move_ids = self.move.split(move, 5, unit, count=4)
-            self.assertEqual(len(move_ids), 4)
-            self.assertEqual([m.quantity for m in self.move.browse(move_ids)],
-                [5, 5, 5, 5])
+            moves = move.split(5, unit, count=4)
+            self.assertEqual(len(moves), 4)
+            self.assertEqual([m.quantity for m in moves], [5, 5, 5, 5])
 
             move = create_move(10)
-            move_ids = self.move.split(move, 5, unit, count=3)
-            self.assertEqual(len(move_ids), 2)
-            self.assertEqual([m.quantity for m in self.move.browse(move_ids)],
-                [5, 5])
+            moves = move.split(5, unit, count=3)
+            self.assertEqual(len(moves), 2)
+            self.assertEqual([m.quantity for m in moves], [5, 5])
 
             move = create_move(10)
-            self.move.write(move.id, {
+            self.move.write([move], {
                     'state': 'assigned',
                     })
-            move_ids = self.move.split(move, 5, unit)
-            self.assertEqual(len(move_ids), 2)
-            self.assertEqual([m.quantity for m in self.move.browse(move_ids)],
-                [5, 5])
-            self.assertEqual([m.state for m in self.move.browse(move_ids)],
+            moves = move.split(5, unit)
+            self.assertEqual(len(moves), 2)
+            self.assertEqual([m.quantity for m in moves], [5, 5])
+            self.assertEqual([m.state for m in moves],
                 ['assigned', 'assigned'])
 
 
diff -r bd39479d70b4 trytond/modules/stock_supply/__init__.py
--- a/trytond/modules/stock_supply/__init__.py	Thu Sep 20 16:16:56 2012 +0200
+++ b/trytond/modules/stock_supply/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,6 +1,24 @@
 #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.pool import Pool
 from .order_point import *
 from .product import *
 from .purchase_request import *
 from .shipment import *
+
+
+def register():
+    Pool.register(
+        OrderPoint,
+        Product,
+        PurchaseRequest,
+        CreatePurchaseRequestStart,
+        CreatePurchaseAskTerm,
+        CreatePurchaseAskParty,
+        ShipmentInternal,
+        module='stock_supply', type_='model')
+    Pool.register(
+        CreatePurchaseRequest,
+        CreatePurchase,
+        module='stock_supply', type_='wizard')
diff -r bd39479d70b4 trytond/modules/stock_supply/order_point.py
--- a/trytond/modules/stock_supply/order_point.py	Thu Sep 20 16:16:56 2012 +0200
+++ b/trytond/modules/stock_supply/order_point.py	Mon Sep 24 20:36:43 2012 +0200
@@ -3,19 +3,19 @@
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.pyson import If, Equal, Eval, Not, In, Get
 from trytond.transaction import Transaction
-from trytond.pool import Pool
 from trytond.backend import TableHandler
 
+__all__ = ['OrderPoint']
+
 
 class OrderPoint(ModelSQL, ModelView):
     """
-    Order Point: Provide a way to define a supply policy for each
+    Order Point
+    Provide a way to define a supply policy for each
     product on each locations. Order points on warehouse are
     considered by the supply scheduler to generate purchase requests.
     """
-    _name = 'stock.order_point'
-    _description = "Order Point"
-
+    __name__ = 'stock.order_point'
     product = fields.Many2One('product.product', 'Product', required=True,
         select=True,
         domain=[
@@ -69,18 +69,19 @@
     unit_digits = fields.Function(fields.Integer('Unit Digits'),
             'get_unit_digits')
 
-    def __init__(self):
-        super(OrderPoint, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(OrderPoint, cls).__setup__()
+        cls._constraints += [
             ('check_concurrent_internal', 'concurrent_internal_op'),
             ('check_uniqueness', 'unique_op'),
             ]
-        self._sql_constraints += [
+        cls._sql_constraints += [
             ('check_max_qty_greater_min_qty',
                 'CHECK(max_quantity >= min_quantity)',
                 'Maximal quantity must be bigger than Minimal quantity'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
                 'unique_op': 'Only one order point is allowed '\
                     'for each product-location pair.',
                 'concurrent_internal_op': 'You can not define ' \
@@ -88,68 +89,63 @@
                     'with opposite locations.',
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 2.2
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.drop_constraint('check_min_max_quantity')
 
-        super(OrderPoint, self).init(module_name)
+        super(OrderPoint, cls).__register__(module_name)
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return "purchase"
 
-    def on_change_product(self, vals):
-        product_obj = Pool().get('product.product')
+    def on_change_product(self):
         res = {
             'unit': None,
             'unit.rec_name': '',
             'unit_digits': 2,
-        }
-        if vals.get('product'):
-            product = product_obj.browse(vals['product'])
-            res['unit'] = product.default_uom.id
-            res['unit.rec_name'] = product.default_uom.rec_name
-            res['unit_digits'] = product.default_uom.digits
+            }
+        if self.product:
+            res['unit'] = self.product.default_uom.id
+            res['unit.rec_name'] = self.product.default_uom.rec_name
+            res['unit_digits'] = self.product.default_uom.digits
         return res
 
-    def get_unit(self, ids, name):
-        res = {}
-        for order in self.browse(ids):
-            res[order.id] = order.product.default_uom.id
-        return res
+    def get_unit(self, name):
+        return self.product.default_uom.id
 
-    def get_unit_digits(self, ids, name):
-        res = {}
-        for order in self.browse(ids):
-            res[order.id] = order.product.default_uom.digits
-        return res
+    def get_unit_digits(self, name):
+        return self.product.default_uom.digits
 
-    def check_concurrent_internal(self, ids):
+    @classmethod
+    def check_concurrent_internal(cls, orders):
         """
         Ensure that there is no 'concurrent' internal order
         points. I.E. no two order point with opposite location for the
         same product and same company.
         """
-        internal_ids = self.search([
-            ('id', 'in', ids),
-            ('type', '=', 'internal'),
-            ])
-        if not internal_ids:
+        internals = cls.search([
+                ('id', 'in', [o.id for o in orders]),
+                ('type', '=', 'internal'),
+                ])
+        if not internals:
             return True
 
         query = ['OR']
-        for op in self.browse(internal_ids):
+        for op in internals:
             arg = ['AND',
                    ('provisioning_location', '=', op.storage_location.id),
                    ('storage_location', '=', op.provisioning_location.id),
                    ('company', '=', op.company.id),
                    ('type', '=', 'internal')]
             query.append(arg)
-        ids = self.search(query)
-        return not bool(ids)
+        return not bool(cls.search(query))
 
-    def _type2field(self, type=None):
+    @staticmethod
+    def _type2field(type=None):
         t2f = {
             'purchase': 'warehouse_location',
             'internal': 'storage_location',
@@ -159,36 +155,30 @@
         else:
             return t2f[type]
 
-    def check_uniqueness(self, ids):
+    @classmethod
+    def check_uniqueness(cls, orders):
         """
         Ensure uniqueness of order points. I.E that there is no several
         order point for the same location, the same product and the
         same company.
         """
         query = ['OR']
-        for op in self.browse(ids):
-            field = self._type2field(op.type)
+        for op in orders:
+            field = cls._type2field(op.type)
             arg = ['AND',
                 ('product', '=', op.product.id),
-                (field, '=', op[field].id),
+                (field, '=', getattr(op, field).id),
                 ('id', '!=', op.id),
                 ('company', '=', op.company.id),
                 ]
             query.append(arg)
-        ids = self.search(query)
-        return not bool(ids)
+        return not bool(cls.search(query))
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        res = {}
-        for op in self.browse(ids):
-            res[op.id] = "%s@%s" % (op.product.name, op.location.name)
-        return res
+    def get_rec_name(self, name):
+        return "%s@%s" % (self.product.name, self.location.name)
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         res = []
         names = clause[2].split('@', 1)
         res.append(('product.template.name', clause[1], names[0]))
@@ -196,27 +186,22 @@
             res.append(('location', clause[1], names[1]))
         return res
 
-    def get_location(self, ids, name):
-        res = {}
-        for op in self.browse(ids):
-            if op.type == 'purchase':
-                res[op.id] = op.warehouse_location.id
-            elif op.type == 'internal':
-                res[op.id] = op.storage_location.id
-            else:
-                res[op.id] = None
-        return res
+    def get_location(self, name):
+        if self.type == 'purchase':
+            return self.warehouse_location.id
+        elif self.type == 'internal':
+            return self.storage_location.id
 
-    def search_location(self, name, domain=None):
+    @classmethod
+    def search_location(cls, name, domain=None):
         ids = []
-        for type, field in self._type2field().iteritems():
+        for type, field in cls._type2field().iteritems():
             args = [('type', '=', type)]
             for _, operator, operand in domain:
                 args.append((field, operator, operand))
-            ids.extend(self.search(args))
+            ids.extend([o.id for o in cls.search(args)])
         return [('id', 'in', ids)]
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
-
-OrderPoint()
diff -r bd39479d70b4 trytond/modules/stock_supply/product.py
--- a/trytond/modules/stock_supply/product.py	Thu Sep 20 16:16:56 2012 +0200
+++ b/trytond/modules/stock_supply/product.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,12 +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.
-from trytond.model import ModelView, ModelSQL, fields
+from trytond.model import fields
+from trytond.pool import PoolMeta
 
+__all__ = ['Product']
+__metaclass__ = PoolMeta
 
-class Product(ModelSQL, ModelView):
-    _name = "product.product"
 
+class Product:
+    __name__ = "product.product"
     order_points = fields.One2Many(
         'stock.order_point', 'product', 'Order Points')
-
-Product()
diff -r bd39479d70b4 trytond/modules/stock_supply/purchase_request.py
--- a/trytond/modules/stock_supply/purchase_request.py	Thu Sep 20 16:16:56 2012 +0200
+++ b/trytond/modules/stock_supply/purchase_request.py	Mon Sep 24 20:36:43 2012 +0200
@@ -12,12 +12,14 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['PurchaseRequest',
+    'CreatePurchaseRequestStart', 'CreatePurchaseRequest',
+    'CreatePurchaseAskTerm', 'CreatePurchaseAskParty', 'CreatePurchase']
+
 
 class PurchaseRequest(ModelSQL, ModelView):
     'Purchase Request'
-    _name = 'purchase.request'
-    _description = __doc__
-
+    __name__ = 'purchase.request'
     product = fields.Many2One('product.product', 'Product', required=True,
         select=True, readonly=True, domain=[('purchasable', '=', True)])
     party = fields.Many2One('party.party', 'Party',  select=True)
@@ -56,39 +58,37 @@
         ('cancel', 'Cancel'),
         ], 'State'), 'get_state')
 
-    def __init__(self):
-        super(PurchaseRequest, self).__init__()
-        self._order[0] = ('id', 'DESC')
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(PurchaseRequest, cls).__setup__()
+        cls._order[0] = ('id', 'DESC')
+        cls._error_messages.update({
                 'create_request': 'Purchase requests are only created ' \
                     'by the system.',
                 })
-        self._sql_constraints += [
+        cls._sql_constraints += [
             ('check_purchase_request_quantity', 'CHECK(quantity > 0)',
                 'The requested quantity must be greater than 0'),
             ]
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        super(PurchaseRequest, self).init(module_name)
+        super(PurchaseRequest, cls).__register__(module_name)
 
         # Migration from 2.0: empty order point origin is -1 instead of 0
         cursor.execute('UPDATE "%s" '
-            'SET origin = %%s WHERE origin = %%s' % self._table,
+            'SET origin = %%s WHERE origin = %%s' % cls._table,
             ('stock.order_point,-1', 'stock.order_point,0'))
 
-    def get_rec_name(self, ids, name):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        res = {}
-        for pr in self.browse(ids):
-            if pr.warehouse:
-                res[pr.id] = "%s@%s" % (pr.product.name, pr.warehouse.name)
-            else:
-                res[pr.id] = pr.product.name
-        return res
+    def get_rec_name(self, name):
+        if self.warehouse:
+            return "%s@%s" % (self.product.name, self.warehouse.name)
+        else:
+            return self.product.name
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         res = []
         names = clause[2].split('@', 1)
         res.append(('product.template.name', clause[1], names[0]))
@@ -96,86 +96,79 @@
             res.append(('warehouse', clause[1], names[1]))
         return res
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def get_purchase(self, ids, name):
-        res = {}
+    def get_purchase(self, name):
+        if self.purchase_line:
+            return self.purchase_line.purchase.id
 
-        requests = self.browse(ids)
-        for request in requests:
-            if request.purchase_line:
-                res[request.id] = request.purchase_line.purchase.id
+    def get_state(self, name):
+        if self.purchase_line:
+            if self.purchase_line.purchase.state == 'cancel':
+                return 'cancel'
+            elif self.purchase_line.purchase.state == 'done':
+                return 'done'
             else:
-                res[request.id] = None
-        return res
+                return 'purchased'
+        return 'draft'
 
-    def get_state(self, ids, name):
-        res = {}.fromkeys(ids, 'draft')
-        for request in self.browse(ids):
-            if request.purchase_line:
-                if request.purchase_line.purchase.state == 'cancel':
-                    res[request.id] = 'cancel'
-                elif request.purchase_line.purchase.state == 'done':
-                    res[request.id] = 'done'
-                else:
-                    res[request.id] = 'purchased'
-        return res
+    def get_warehouse_required(self, name):
+        return self.product.type in ('goods', 'assets')
 
-    def get_warehouse_required(self, ids, name):
-        requireds = {}
-        for request in self.browse(ids):
-            requireds[request.id] = request.product.type in ('goods', 'assets')
-        return requireds
-
-    def origin_get(self):
-        model_obj = Pool().get('ir.model')
+    @staticmethod
+    def origin_get():
+        Model = Pool().get('ir.model')
         res = []
-        model_ids = model_obj.search([
-            ('model', '=', 'stock.order_point'),
-            ])
-        for model in model_obj.browse(model_ids):
+        models = Model.search([
+                ('model', '=', 'stock.order_point'),
+                ])
+        for model in models:
             res.append([model.model, model.name])
         return res
 
-    def generate_requests(self):
+    @classmethod
+    def generate_requests(cls):
         """
         For each product compute the purchase request that must be
         create today to meet product outputs.
         """
         pool = Pool()
-        order_point_obj = pool.get('stock.order_point')
-        product_obj = pool.get('product.product')
-        location_obj = pool.get('stock.location')
-        user_obj = pool.get('res.user')
-        company = user_obj.browse(Transaction().user).company
+        OrderPoint = pool.get('stock.order_point')
+        Product = pool.get('product.product')
+        Location = pool.get('stock.location')
+        User = pool.get('res.user')
+        company = User(Transaction().user).company
 
         # fetch warehouses:
-        warehouse_ids = location_obj.search([
+        warehouses = Location.search([
                 ('type', '=', 'warehouse'),
                 ])
+        warehouse_ids = [w.id for w in warehouses]
         # fetch order points
-        order_point_ids = order_point_obj.search([
+        order_points = OrderPoint.search([
             ('type', '=', 'purchase'),
             ])
         # index them by product
         product2ops = {}
-        for order_point in order_point_obj.browse(order_point_ids):
+        for order_point in order_points:
             product2ops[
                 (order_point.warehouse_location.id, order_point.product.id)
                 ] = order_point
 
         # fetch goods and assets
         # ordered by ids to speedup reduce_ids in products_by_location
-        product_ids = product_obj.search([
+        products = Product.search([
                 ('type', 'in', ['goods', 'assets']),
                 ('consumable', '=', False),
                 ('purchasable', '=', True),
                 ], order=[('id', 'ASC')])
+        product_ids = [p.id for p in products]
         #aggregate product by minimum supply date
         date2products = {}
-        for product in product_obj.browse(product_ids):
-            min_date, max_date = self.get_supply_dates(product)
+        for product in products:
+            min_date, max_date = cls.get_supply_dates(product)
             date2products.setdefault((min_date, max_date), []).append(product)
 
         # compute requests
@@ -187,13 +180,13 @@
                 product_ids = [p.id for p in products[i:i + cursor.IN_MAX]]
                 with Transaction().set_context(forecast=True,
                         stock_date_end=min_date or datetime.date.max):
-                    pbl = product_obj.products_by_location(warehouse_ids,
+                    pbl = Product.products_by_location(warehouse_ids,
                         product_ids, with_childs=True, skip_zero=False)
                 for warehouse_id in warehouse_ids:
                     min_date_qties = dict((x, pbl.pop((warehouse_id, x)))
                         for x in product_ids)
                     # Search for shortage between min-max
-                    shortages = self.get_shortage(warehouse_id, product_ids,
+                    shortages = cls.get_shortage(warehouse_id, product_ids,
                         min_date, max_date, min_date_qties=min_date_qties,
                         order_points=product2ops)
 
@@ -204,53 +197,44 @@
                         order_point = product2ops.get(
                             (warehouse_id, product.id))
                         # generate request values
-                        request_val = self.compute_request(product,
+                        request = cls.compute_request(product,
                             warehouse_id, shortage_date, product_quantity,
                             company, order_point)
-                        new_requests.append(request_val)
+                        new_requests.append(request)
 
-        new_requests = self.compare_requests(new_requests)
+        new_requests = cls.compare_requests(new_requests)
 
-        self.create_requests(new_requests)
-        return {}
+        cls.create_requests(new_requests)
 
-    def create_requests(self, new_requests):
-        request_obj = Pool().get('purchase.request')
+    @classmethod
+    def create_requests(cls, new_requests):
+        for new_req in new_requests:
+            if new_req.supply_date == datetime.date.max:
+                new_req.supply_date = None
+            if new_req.quantity > 0.0:
+                new_req.save()
 
-        for new_req in new_requests:
-            if new_req['supply_date'] == datetime.date.max:
-                new_req['supply_date'] = None
-            if new_req['quantity'] > 0.0:
-                new_req.update({
-                        'product': new_req['product'].id,
-                        'party': new_req['party'] and new_req['party'].id,
-                        'uom': new_req['uom'].id,
-                        'computed_uom': new_req['computed_uom'].id,
-                        'company': new_req['company'].id
-                        })
-                request_obj.create(new_req)
-
-    def compare_requests(self, new_requests):
+    @classmethod
+    def compare_requests(cls, new_requests):
         """
         Compare new_requests with already existing request to avoid
         to re-create existing requests.
         """
         # delete purchase request without a purchase line
         pool = Pool()
-        uom_obj = pool.get('product.uom')
-        request_obj = pool.get('purchase.request')
-        req_ids = request_obj.search([
+        Uom = pool.get('product.uom')
+        Request = pool.get('purchase.request')
+        reqs = Request.search([
             ('purchase_line', '=', None),
             ('origin', 'like', 'stock.order_point,%'),
             ])
-        request_obj.delete(req_ids)
+        Request.delete(reqs)
 
-        req_ids = request_obj.search([
+        requests = Request.search([
                 ('purchase_line.moves', '=', None),
                 ('purchase_line.purchase.state', '!=', 'cancel'),
                 ('origin', 'like', 'stock.order_point,%'),
                 ])
-        requests = request_obj.browse(req_ids)
         # Fetch data from existing requests
         existing_req = {}
         for request in requests:
@@ -260,7 +244,7 @@
                     request.warehouse.id != pline.purchase.warehouse.id:
                 continue
             # Take smallest amount between request and purchase line
-            req_qty = uom_obj.compute_qty(request.uom, request.quantity,
+            req_qty = Uom.compute_qty(request.uom, request.quantity,
                     pline.unit)
             if req_qty < pline.quantity:
                 quantity = request.quantity
@@ -282,46 +266,40 @@
             i.sort(lambda r, s: cmp(r['supply_date'], s['supply_date']))
 
         # Update new requests to take existing requests into account
-        new_requests.sort(key=operator.itemgetter('supply_date'))
+        new_requests.sort(key=operator.attrgetter('supply_date'))
         for new_req in new_requests:
-            for old_req in existing_req.get((new_req['product'].id,
-                                             new_req['warehouse']), []):
-                if old_req['supply_date'] <= new_req['supply_date']:
-                    quantity = uom_obj.compute_qty(old_req['uom'],
-                            old_req['quantity'], new_req['uom'])
-                    new_req['quantity'] = max(0.0,
-                        new_req['quantity'] - quantity)
-                    new_req['computed_quantity'] = new_req['quantity']
-                    old_req['quantity'] = uom_obj.compute_qty(new_req['uom'],
-                        max(0.0, quantity - new_req['quantity']),
-                        old_req['uom'])
+            for old_req in existing_req.get(
+                    (new_req.product.id, new_req.warehouse.id), []):
+                if old_req['supply_date'] <= new_req.supply_date:
+                    quantity = Uom.compute_qty(old_req['uom'],
+                        old_req['quantity'], new_req.uom)
+                    new_req.quantity = max(0.0, new_req.quantity - quantity)
+                    new_req.computed_quantity = new_req.quantity
+                    old_req['quantity'] = Uom.compute_qty(new_req.uom,
+                        max(0.0, quantity - new_req.quantity), old_req['uom'])
                 else:
                     break
 
         return new_requests
 
-    def get_supply_dates(self, product):
+    @classmethod
+    def get_supply_dates(cls, product):
         """
         Return the minimal interval of earliest supply dates for a product.
-
-        :param product: a BrowseRecord of the Product
-        :return: a tuple with the two dates
         """
-        product_supplier_obj = Pool().get('purchase.product_supplier')
-        date_obj = Pool().get('ir.date')
+        Date = Pool().get('ir.date')
 
         min_date = None
         max_date = None
-        today = date_obj.today()
+        today = Date.today()
 
         for product_supplier in product.product_suppliers:
-            supply_date = product_supplier_obj.compute_supply_date(
-                    product_supplier, date=today)
+            supply_date = product_supplier.compute_supply_date(date=today)
             # TODO next_day is by default today + 1 but should depends
             # on the CRON activity
             next_day = today + datetime.timedelta(1)
-            next_supply_date = product_supplier_obj.compute_supply_date(
-                            product_supplier, date=next_day)
+            next_supply_date = product_supplier.compute_supply_date(
+                date=next_day)
             if (not min_date) or supply_date < min_date:
                 min_date = supply_date
             if (not max_date):
@@ -337,20 +315,18 @@
 
         return (min_date, max_date)
 
-    def find_best_supplier(self, product, date):
+    @classmethod
+    def find_best_supplier(cls, product, date):
         '''
         Return the best supplier and purchase_date for the product.
         '''
-        pool = Pool()
-        date_obj = pool.get('ir.date')
-        product_supplier_obj = pool.get('purchase.product_supplier')
+        Date = Pool().get('ir.date')
 
         supplier = None
         timedelta = datetime.timedelta.max
-        today = date_obj.today()
+        today = Date.today()
         for product_supplier in product.product_suppliers:
-            supply_date = product_supplier_obj.compute_supply_date(
-                    product_supplier, date=today)
+            supply_date = product_supplier.compute_supply_date(date=today)
             sup_timedelta = date - supply_date
             if not supplier:
                 supplier = product_supplier.party
@@ -364,13 +340,13 @@
                 timedelta = sup_timedelta
 
         if supplier:
-            purchase_date = product_supplier_obj.compute_purchase_date(
-                    product_supplier, date)
+            purchase_date = product_supplier.compute_purchase_date(date)
         else:
             purchase_date = today
         return supplier, purchase_date
 
-    def compute_request(self, product, location_id, shortage_date,
+    @classmethod
+    def compute_request(cls, product, location_id, shortage_date,
             product_quantity, company, order_point=None):
         """
         Return the value of the purchase request which will answer to
@@ -379,13 +355,14 @@
         supplier.
         """
         pool = Pool()
-        uom_obj = pool.get('product.uom')
+        Uom = pool.get('product.uom')
+        Request = pool.get('purchase.request')
 
-        supplier, purchase_date = self.find_best_supplier(product,
+        supplier, purchase_date = cls.find_best_supplier(product,
             shortage_date)
 
         max_quantity = order_point and order_point.max_quantity or 0.0
-        quantity = uom_obj.compute_qty(product.default_uom,
+        quantity = Uom.compute_qty(product.default_uom,
                 max_quantity - product_quantity,
                 product.purchase_uom or product.default_uom)
 
@@ -393,21 +370,22 @@
             origin = 'stock.order_point,%s' % order_point.id
         else:
             origin = 'stock.order_point,-1'
-        return {'product': product,
-                'party': supplier and supplier or None,
-                'quantity': quantity,
-                'uom': product.purchase_uom or product.default_uom,
-                'computed_quantity': quantity,
-                'computed_uom': product.purchase_uom or product.default_uom,
-                'purchase_date': purchase_date,
-                'supply_date': shortage_date,
-                'stock_level': product_quantity,
-                'company': company,
-                'warehouse': location_id,
-                'origin': origin,
-                }
+        return Request(product=product,
+            party=supplier and supplier or None,
+            quantity=quantity,
+            uom=product.purchase_uom or product.default_uom,
+            computed_quantity=quantity,
+            computed_uom=product.purchase_uom or product.default_uom,
+            purchase_date=purchase_date,
+            supply_date=shortage_date,
+            stock_level=product_quantity,
+            company=company,
+            warehouse=location_id,
+            origin=origin,
+            )
 
-    def get_shortage(self, location_id, product_ids, min_date, max_date,
+    @classmethod
+    def get_shortage(cls, location_id, product_ids, min_date, max_date,
             min_date_qties, order_points):
         """
         Return for each product the first date between min_date and max_date
@@ -420,7 +398,7 @@
         min_date_qty is the quantities for each products at the min_date.
         order_points is a dictionary that links products to order point.
         """
-        product_obj = Pool().get('product.product')
+        Product = Pool().get('product.product')
 
         res_dates = {}
         res_qties = {}
@@ -449,7 +427,7 @@
 
             with Transaction().set_context(stock_date_start=current_date,
                     stock_date_end=current_date):
-                pbl = product_obj.products_by_location([location_id],
+                pbl = Product.products_by_location([location_id],
                     product_ids, with_childs=True, skip_zero=False)
             for key, qty in pbl.iteritems():
                 _, product_id = key
@@ -461,27 +439,22 @@
         return dict((x, (res_dates.get(x), res_qties.get(x)))
             for x in product_ids)
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         for field_name in ('product', 'quantity', 'uom', 'company'):
             if not vals.get(field_name):
-                self.raise_user_error('create_request')
-        return super(PurchaseRequest, self).create(vals)
-
-PurchaseRequest()
+                cls.raise_user_error('create_request')
+        return super(PurchaseRequest, cls).create(vals)
 
 
 class CreatePurchaseRequestStart(ModelView):
     'Create Purchase Request'
-    _name = 'purchase.request.create.start'
-    _description = __doc__
-
-CreatePurchaseRequestStart()
+    __name__ = 'purchase.request.create.start'
 
 
 class CreatePurchaseRequest(Wizard):
     'Create Purchase Request'
-    _name = 'purchase.request.create'
-
+    __name__ = 'purchase.request.create'
     start = StateView('purchase.request.create.start',
         'stock_supply.purchase_request_create_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -489,44 +462,35 @@
             ])
     create_ = StateAction('stock_supply.act_purchase_request_form_draft')
 
-    def do_create_(self, session, action):
-        purchase_request_obj = Pool().get('purchase.request')
-        purchase_request_obj.generate_requests()
+    def do_create_(self, action):
+        PurchaseRequest = Pool().get('purchase.request')
+        PurchaseRequest.generate_requests()
         return action, {}
 
-    def transition_create_(self, session):
+    def transition_create_(self):
         return 'end'
 
-CreatePurchaseRequest()
-
 
 class CreatePurchaseAskTerm(ModelView):
     'Create Purchase Ask Term'
-    _name = 'purchase.request.create_purchase.ask_term'
-    _description = __doc__
+    __name__ = 'purchase.request.create_purchase.ask_term'
     party = fields.Many2One('party.party', 'Supplier', readonly=True)
     company = fields.Many2One('company.company', 'Company', readonly=True)
     payment_term = fields.Many2One(
         'account.invoice.payment_term', 'Payment Term', required=True)
 
-CreatePurchaseAskTerm()
-
 
 class CreatePurchaseAskParty(ModelView):
     'Create Purchase Ask Party'
-    _name = 'purchase.request.create_purchase.ask_party'
-    _description = __doc__
+    __name__ = 'purchase.request.create_purchase.ask_party'
     product = fields.Many2One('product.product', 'Product', readonly=True)
     company = fields.Many2One('company.company', 'Company', readonly=True)
     party = fields.Many2One('party.party', 'Supplier', required=True)
 
-CreatePurchaseAskParty()
-
 
 class CreatePurchase(Wizard):
     'Create Purchase'
-    _name = 'purchase.request.create_purchase'
-
+    __name__ = 'purchase.request.create_purchase'
     start = StateTransition()
     ask_party = StateView('purchase.request.create_purchase.ask_party',
         'stock_supply.purchase_request_create_purchase_ask_party', [
@@ -539,17 +503,18 @@
             Button('Continue', 'start', 'tryton-go-next', default=True),
             ])
 
-    def __init__(self):
-        super(CreatePurchase, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(CreatePurchase, cls).__setup__()
+        cls._error_messages.update({
             'missing_price': 'Purchase price is missing for product: %s ' \
                 '(id: %s)!',
             'please_update': 'This price is necessary for creating purchase.'
             })
 
-    def default_ask_party(self, session, fields):
-        request_obj = Pool().get('purchase.request')
-        requests = request_obj.browse(Transaction().context['active_ids'])
+    def default_ask_party(self, fields):
+        Request = Pool().get('purchase.request')
+        requests = Request.browse(Transaction().context['active_ids'])
         for request in requests:
             if request.purchase_line:
                 continue
@@ -563,9 +528,9 @@
             'company': request.company.id,
             }
 
-    def default_ask_term(self, session, fields):
-        request_obj = Pool().get('purchase.request')
-        requests = request_obj.browse(Transaction().context['active_ids'])
+    def default_ask_term(self, fields):
+        Request = Pool().get('purchase.request')
+        requests = Request.browse(Transaction().context['active_ids'])
         for request in requests:
             if (not request.party) or request.purchase_line:
                 continue
@@ -579,75 +544,68 @@
             'company': request.company.id,
             }
 
-    def _group_purchase_key(self, requests, request):
+    @staticmethod
+    def _group_purchase_key(requests, request):
         '''
         The key to group lines by purchases
-
-        :param requests: a list of requests
-        :param request: the request
-
-        :return: a list of key-value as tuples of the purchase
+        A list of key-value as tuples of the purchase
         '''
-        party_obj = Pool().get('party.party')
-
         return (
-            ('company', request.company.id),
-            ('party', request.party.id),
-            ('payment_term', request.party.supplier_payment_term.id),
-            ('warehouse', request.warehouse.id),
+            ('company', request.company),
+            ('party', request.party),
+            ('payment_term', request.party.supplier_payment_term),
+            ('warehouse', request.warehouse),
             # XXX use function field
-            ('currency', request.company.currency.id),
-            ('invoice_address', party_obj.address_get(request.party.id,
-                    type='invoice')),
+            ('currency', request.company.currency),
+            ('invoice_address', request.party.address_get(type='invoice')),
             )
 
-    def transition_start(self, session):
+    def transition_start(self):
         pool = Pool()
-        request_obj = pool.get('purchase.request')
-        party_obj = pool.get('party.party')
-        purchase_obj = pool.get('purchase.purchase')
-        line_obj = pool.get('purchase.line')
-        date_obj = pool.get('ir.date')
+        Request = pool.get('purchase.request')
+        Party = pool.get('party.party')
+        Purchase = pool.get('purchase.purchase')
+        Date = pool.get('ir.date')
 
         request_ids = Transaction().context['active_ids']
 
-        if (session.ask_party.product
-                and session.ask_party.party
-                and session.ask_party.company):
-            req_ids = request_obj.search([
+        if (getattr(self.ask_party, 'product', None)
+                and getattr(self.ask_party, 'party', None)
+                and getattr(self.ask_party, 'company', None)):
+            reqs = Request.search([
                     ('id', 'in', request_ids),
                     ('party', '=', None),
                     ])
-            if req_ids:
-                request_obj.write(req_ids, {
-                        'party': session.ask_party.party.id,
+            if reqs:
+                Request.write(reqs, {
+                        'party': self.ask_party.party.id,
                         })
-            session.ask_party.product = None
-            session.ask_party.party = None
-            session.ask_party.company = None
-        elif (session.ask_term.payment_term
-                and session.ask_term.party
-                and session.ask_term.company):
+            self.ask_party.product = None
+            self.ask_party.party = None
+            self.ask_party.company = None
+        elif (getattr(self.ask_term, 'payment_term', None)
+                and getattr(self.ask_term, 'party', None)
+                and getattr(self.ask_term, 'company', None)):
             with Transaction().set_context(
-                    company=session.ask_term.company.id):
-                party_obj.write(session.ask_term.party.id, {
+                    company=self.ask_term.company.id):
+                Party.write([self.ask_term.party], {
                         'supplier_payment_term': \
-                            session.ask_term.payment_term.id,
+                            self.ask_term.payment_term.id,
                         })
-            session.ask_term.payment_term = None
-            session.ask_term.party = None
-            session.ask_term.company = None
+            self.ask_term.payment_term = None
+            self.ask_term.party = None
+            self.ask_term.company = None
 
-        req_ids = request_obj.search([
+        reqs = Request.search([
                 ('id', 'in', request_ids),
                 ('purchase_line', '=', None),
                 ('party', '=', None),
                 ])
-        if req_ids:
+        if reqs:
             return 'ask_party'
 
-        today = date_obj.today()
-        requests = request_obj.browse(request_ids)
+        today = Date.today()
+        requests = Request.browse(request_ids)
 
         requests = [r for r in requests if not r.purchase_line]
         if any(r for r in requests if not r.party.supplier_payment_term):
@@ -667,68 +625,61 @@
                     purchase_date = today
                 if purchase_date < today:
                     purchase_date = today
-                values = {
-                    'purchase_date': purchase_date,
-                    }
-                values.update(dict(key))
-                purchase_id = purchase_obj.create(values)
+                purchase = Purchase(purchase_date=purchase_date)
+                for f, v in key:
+                    setattr(purchase, f, v)
+                purchase.save()
                 for request in grouped_requests:
-                    values = self.compute_purchase_line(request)
-                    values['purchase'] = purchase_id
-                    line_id = line_obj.create(values)
-                    request_obj.write(request.id, {
-                            'purchase_line': line_id,
+                    line = self.compute_purchase_line(request)
+                    line.purchase = purchase
+                    line.save()
+                    Request.write([request], {
+                            'purchase_line': line.id,
                             })
         return 'end'
 
-    def _get_tax_rule_pattern(self, request):
+    @staticmethod
+    def _get_tax_rule_pattern(request):
         '''
         Get tax rule pattern
+        '''
+        return {}
 
-        :param request: the BrowseRecord of the purchase request
-        :return: a dictionary to use as pattern for tax rule
-        '''
-        res = {}
-        return res
+    @classmethod
+    def compute_purchase_line(cls, request):
+        pool = Pool()
+        Product = pool.get('product.product')
+        Line = pool.get('purchase.line')
 
-    def compute_purchase_line(self, request):
-        pool = Pool()
-        product_obj = pool.get('product.product')
-        tax_rule_obj = pool.get('account.tax.rule')
-        line_obj = pool.get('purchase.line')
-
-        line = {
-            'product': request.product.id,
-            'unit': request.uom.id,
-            'quantity': request.quantity,
-            'description': request.product.name,
-            }
+        line = Line(
+            product=request.product,
+            unit=request.uom,
+            quantity=request.quantity,
+            description=request.product.name,
+            )
 
         # XXX purchase with several lines of the same product
         with Transaction().set_context(uom=request.uom.id,
                 supplier=request.party.id,
                 currency=request.company.currency.id):
-            product_price = product_obj.get_purchase_price(
-                    [request.product.id], request.quantity)[request.product.id]
+            product_price = Product.get_purchase_price(
+                [request.product], request.quantity)[request.product.id]
             product_price = product_price.quantize(
-                Decimal(1) / 10 ** line_obj.unit_price.digits[1])
+                Decimal(1) / 10 ** Line.unit_price.digits[1])
 
         if product_price is None:
-            self.raise_user_error('missing_price', (request.product.name,
+            cls.raise_user_error('missing_price', (request.product.name,
                     request.product.id), 'please_update')
-        line['unit_price'] = product_price
+        line.unit_price = product_price
 
         taxes = []
         for tax in request.product.supplier_taxes_used:
             if request.party and request.party.supplier_tax_rule:
-                pattern = self._get_tax_rule_pattern(request)
-                tax_ids = tax_rule_obj.apply(request.party.supplier_tax_rule,
-                        tax, pattern)
+                pattern = cls._get_tax_rule_pattern(request)
+                tax_ids = request.party.supplier_tax_rule.apply(tax, pattern)
                 if tax_ids:
                     taxes.extend(tax_ids)
                 continue
             taxes.append(tax.id)
-        line['taxes'] = [('add', taxes)]
+        line.taxes = taxes
         return line
-
-CreatePurchase()
diff -r bd39479d70b4 trytond/modules/stock_supply/shipment.py
--- a/trytond/modules/stock_supply/shipment.py	Thu Sep 20 16:16:56 2012 +0200
+++ b/trytond/modules/stock_supply/shipment.py	Mon Sep 24 20:36:43 2012 +0200
@@ -2,13 +2,17 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelView, ModelSQL
 from trytond.transaction import Transaction
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
+
+__all__ = ['ShipmentInternal']
+__metaclass__ = PoolMeta
 
 
 class ShipmentInternal(ModelSQL, ModelView):
-    _name = 'stock.shipment.internal'
+    __name__ = 'stock.shipment.internal'
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.2: packing renamed into shipment
         cursor.execute("UPDATE ir_model_data "\
@@ -19,26 +23,27 @@
                 "SET model = REPLACE(model, 'packing', 'shipment') "\
                 "WHERE model like '%%packing%%' AND module = %s",
                 (module_name,))
-        super(ShipmentInternal, self).init(module_name)
+        super(ShipmentInternal, cls).__register__(module_name)
 
-    def generate_internal_shipment(self):
+    @classmethod
+    def generate_internal_shipment(cls):
         """
         Generate internal shipments to meet order points defined on
         non-warehouse location.
         """
         pool = Pool()
-        order_point_obj = pool.get('stock.order_point')
-        uom_obj = pool.get('product.uom')
-        product_obj = pool.get('product.product')
-        date_obj = pool.get('ir.date')
-        user_obj = pool.get('res.user')
-        user_record = user_obj.browse(Transaction().user)
-        today = date_obj.today()
+        OrderPoint = pool.get('stock.order_point')
+        Uom = pool.get('product.uom')
+        Product = pool.get('product.product')
+        Date = pool.get('ir.date')
+        User = pool.get('res.user')
+        Move = pool.get('stock.move')
+        user_record = User(Transaction().user)
+        today = Date.today()
         # fetch quantities on order points
-        op_ids = order_point_obj.search([
+        order_points = OrderPoint.search([
             ('type', '=', 'internal'),
             ])
-        order_points = order_point_obj.browse(op_ids)
         id2product = {}
         location_ids = []
         for op in order_points:
@@ -46,7 +51,7 @@
             location_ids.append(op.storage_location.id)
 
         with Transaction().set_context(stock_date_end=today):
-            pbl = product_obj.products_by_location(location_ids,
+            pbl = Product.products_by_location(location_ids,
                 list(id2product.iterkeys()), with_childs=True)
 
         # Create a list of move to create
@@ -60,21 +65,21 @@
                 moves[key] = op.max_quantity - qty
 
         # Compare with existing draft shipments
-        shipment_ids = self.search([
+        shipments = cls.search([
                 ('state', '=', 'draft'),
                 ['OR',
                     ('planned_date', '<=', today),
                     ('planned_date', '=', None),
                     ],
                 ])
-        for shipment in self.browse(shipment_ids):
+        for shipment in shipments:
             for move in shipment.moves:
                 key = (shipment.to_location.id,
                        shipment.from_location.id,
                        move.product.id)
                 if key not in moves:
                     continue
-                quantity = uom_obj.compute_qty(move.uom, move.quantity,
+                quantity = Uom.compute_qty(move.uom, move.quantity,
                     id2product[move.product.id].default_uom)
                 moves[key] = max(0, moves[key] - quantity)
 
@@ -85,27 +90,22 @@
             shipments.setdefault(
                 (from_location, to_location), []).append((product, qty))
         # Create shipments and moves
-        for shipment, moves in shipments.iteritems():
-            from_location, to_location = shipment
-            values = {
-                'from_location': from_location,
-                'to_location': to_location,
-                'planned_date': today,
-                'moves': [],
-                }
+        for locations, moves in shipments.iteritems():
+            from_location, to_location = locations
+            shipment = cls(
+                from_location=from_location,
+                to_location=to_location,
+                planned_date=today,
+                moves=[],
+                )
             for move in moves:
                 product, qty = move
-                values['moves'].append(
-                    ('create',
-                        {
-                            'from_location': from_location,
-                            'to_location': to_location,
-                            'product': product,
-                            'quantity': qty,
-                            'uom': id2product[product].default_uom.id,
-                            'company': user_record.company.id,
-                            }
+                shipment.moves.append(Move(
+                        from_location=from_location,
+                        to_location=to_location,
+                        product=product,
+                        quantity=qty,
+                        uom=id2product[product].default_uom,
+                        company=user_record.company,
                         ))
-            self.create(values)
-
-ShipmentInternal()
+            shipment.save()
diff -r bd39479d70b4 trytond/modules/stock_supply/tests/test_stock_supply.py
--- a/trytond/modules/stock_supply/tests/test_stock_supply.py	Thu Sep 20 16:16:56 2012 +0200
+++ b/trytond/modules/stock_supply/tests/test_stock_supply.py	Mon Sep 24 20:36:43 2012 +0200
@@ -64,10 +64,7 @@
         '''
         for purchase_date, delivery_time, supply_date in DATES:
             with Transaction().start(DB_NAME, USER, context=CONTEXT):
-                product_supplier_id = self.create_product_supplier(
-                    delivery_time)
-                product_supplier = self.product_supplier.browse(
-                    product_supplier_id)
+                product_supplier = self.create_product_supplier(delivery_time)
                 date = self.product_supplier.compute_supply_date(
                     product_supplier, purchase_date)
                 self.assertEqual(date, supply_date)
@@ -78,10 +75,7 @@
         '''
         for purchase_date, delivery_time, supply_date in DATES:
             with Transaction().start(DB_NAME, USER, context=CONTEXT):
-                product_supplier_id = self.create_product_supplier(
-                    delivery_time)
-                product_supplier = self.product_supplier.browse(
-                    product_supplier_id)
+                product_supplier = self.create_product_supplier(delivery_time)
                 date = self.product_supplier.compute_purchase_date(
                     product_supplier, supply_date)
                 self.assertEqual(date, purchase_date)
@@ -93,48 +87,48 @@
         :param delivery_time: time in days needed to supply
         :return: the id of the Product Supplier
         '''
-        uom_category_id = self.uom_category.create({'name': 'Test'})
-        uom_id = self.uom.create({
+        uom_category = self.uom_category.create({'name': 'Test'})
+        uom = self.uom.create({
             'name': 'Test',
             'symbol': 'T',
-            'category': uom_category_id,
+            'category': uom_category.id,
             'rate': 1.0,
             'factor': 1.0,
             })
-        category_id = self.category.create({'name': 'ProdCategoryTest'})
-        product_id = self.product.create({
+        category = self.category.create({'name': 'ProdCategoryTest'})
+        product = self.product.create({
                 'name': 'ProductTest',
-                'default_uom': uom_id,
-                'category': category_id,
+                'default_uom': uom.id,
+                'category': category.id,
                 'account_category': True,
                 'list_price': Decimal(0),
                 'cost_price': Decimal(0),
                 })
-        company_id, = self.company.search([('name', '=', 'B2CK')])
-        self.user.write(USER, {
-            'main_company': company_id,
-            'company': company_id,
+        company, = self.company.search([('name', '=', 'B2CK')])
+        self.user.write([self.user(USER)], {
+            'main_company': company.id,
+            'company': company.id,
             })
-        receivable_id, = self.account.search([
+        receivable, = self.account.search([
             ('kind', '=', 'receivable'),
-            ('company', '=', company_id),
+            ('company', '=', company.id),
             ])
-        payable_id, = self.account.search([
+        payable, = self.account.search([
             ('kind', '=', 'payable'),
-            ('company', '=', company_id),
+            ('company', '=', company.id),
             ])
-        supplier_id = self.party.create({
+        supplier = self.party.create({
             'name': 'supplier',
-            'account_receivable': receivable_id,
-            'account_payable': payable_id,
+            'account_receivable': receivable.id,
+            'account_payable': payable.id,
             })
-        product_supplier_id = self.product_supplier.create({
-            'product': product_id,
-            'company': company_id,
-            'party': supplier_id,
+        product_supplier = self.product_supplier.create({
+            'product': product.id,
+            'company': company.id,
+            'party': supplier.id,
             'delivery_time': delivery_time,
             })
-        return product_supplier_id
+        return product_supplier
 
 
 def suite():
diff -r a844b23b6c90 trytond/modules/stock_supply_day/__init__.py
--- a/trytond/modules/stock_supply_day/__init__.py	Thu Sep 20 16:18:14 2012 +0200
+++ b/trytond/modules/stock_supply_day/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,4 +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.pool import Pool
 from .purchase import *
+
+
+def register():
+    Pool.register(
+        ProductSupplier,
+        ProductSupplierDay,
+        module='stock_supply_day', type_='model')
diff -r a844b23b6c90 trytond/modules/stock_supply_day/purchase.py
--- a/trytond/modules/stock_supply_day/purchase.py	Thu Sep 20 16:18:14 2012 +0200
+++ b/trytond/modules/stock_supply_day/purchase.py	Mon Sep 24 20:36:43 2012 +0200
@@ -2,18 +2,21 @@
 #this repository contains the full copyright notices and license terms.
 import datetime
 from trytond.model import ModelView, ModelSQL, fields
+from trytond.pool import PoolMeta
 
+__all__ = ['ProductSupplier', 'ProductSupplierDay']
+__metaclass__ = PoolMeta
 
-class ProductSupplier(ModelSQL, ModelView):
-    _name = 'purchase.product_supplier'
+
+class ProductSupplier:
+    __name__ = 'purchase.product_supplier'
     weekdays = fields.One2Many('purchase.product_supplier.day',
         'product_supplier', 'Week Days')
 
-    def compute_supply_date(self, product_supplier, date=None):
-        date = super(ProductSupplier, self).compute_supply_date(
-                product_supplier, date=date)
+    def compute_supply_date(self, date=None):
+        date = super(ProductSupplier, self).compute_supply_date(date=date)
         earlier_date = None
-        for day in product_supplier.weekdays:
+        for day in self.weekdays:
             weekday = int(day.weekday)
             diff = weekday - date.weekday()
             if diff < 0:
@@ -25,9 +28,9 @@
             earlier_date = new_date
         return earlier_date
 
-    def compute_purchase_date(self, product_supplier, date):
+    def compute_purchase_date(self, date):
         later_date = None
-        for day in product_supplier.weekdays:
+        for day in self.weekdays:
             weekday = int(day.weekday)
             diff = (date.weekday() - weekday) % 7
             new_date = date - datetime.timedelta(diff)
@@ -36,18 +39,13 @@
             later_date = new_date
         if later_date:
             date = later_date
-        return super(ProductSupplier, self).compute_purchase_date(
-                product_supplier, date)
-
-ProductSupplier()
+        return super(ProductSupplier, self).compute_purchase_date(date)
 
 
 class ProductSupplierDay(ModelSQL, ModelView):
     'Product Supplier Day'
-    _name = 'purchase.product_supplier.day'
+    __name__ = 'purchase.product_supplier.day'
     _rec_name = 'weekday'
-    _description = __doc__
-
     product_supplier = fields.Many2One('purchase.product_supplier', 'Supplier',
             required=True, ondelete='CASCADE')
     weekday = fields.Selection([
@@ -60,11 +58,11 @@
         ('6', 'Sunday'),
         ], 'Week Day', required=True, sort=False)
 
-    def __init__(self):
-        super(ProductSupplierDay, self).__init__()
-        self._order.insert(0, ('weekday', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ProductSupplierDay, cls).__setup__()
+        cls._order.insert(0, ('weekday', 'ASC'))
 
-    def default_weekday(self):
+    @staticmethod
+    def default_weekday():
         return '0'
-
-ProductSupplierDay()
diff -r a844b23b6c90 trytond/modules/stock_supply_day/tests/test_stock_supply_day.py
--- a/trytond/modules/stock_supply_day/tests/test_stock_supply_day.py	Thu Sep 20 16:18:14 2012 +0200
+++ b/trytond/modules/stock_supply_day/tests/test_stock_supply_day.py	Mon Sep 24 20:36:43 2012 +0200
@@ -69,12 +69,9 @@
             # Monday, the 2011-12-05.
         for purchase_date, delivery_time, weekday, supply_date in dates:
             with Transaction().start(DB_NAME, USER, context=CONTEXT):
-                product_supplier_id = self.create_product_supplier_day(
+                product_supplier = self.create_product_supplier_day(
                     delivery_time, weekday)
-                product_supplier = self.product_supplier.browse(
-                    product_supplier_id)
-                date = self.product_supplier.compute_supply_date(
-                    product_supplier, purchase_date)
+                date = product_supplier.compute_supply_date(purchase_date)
                 self.assertEqual(date, supply_date)
 
     def test0020compute_purchase_date(self):
@@ -97,12 +94,9 @@
             # the forecast purchase date is the 2011-12-27.
         for purchase_date, delivery_time, weekday, supply_date in dates:
             with Transaction().start(DB_NAME, USER, context=CONTEXT):
-                product_supplier_id = self.create_product_supplier_day(
+                product_supplier = self.create_product_supplier_day(
                     delivery_time, weekday)
-                product_supplier = self.product_supplier.browse(
-                    product_supplier_id)
-                date = self.product_supplier.compute_purchase_date(
-                    product_supplier, supply_date)
+                date = product_supplier.compute_purchase_date(supply_date)
                 self.assertEqual(date, purchase_date)
 
     def create_product_supplier_day(self, delivery_time, weekday):
@@ -113,52 +107,52 @@
         :param weekday: supply day of the week (0 - 6)
         :return: the id of the Product Supplier Day
         '''
-        uom_category_id = self.uom_category.create({'name': 'Test'})
-        uom_id = self.uom.create({
+        uom_category = self.uom_category.create({'name': 'Test'})
+        uom = self.uom.create({
             'name': 'Test',
             'symbol': 'T',
-            'category': uom_category_id,
+            'category': uom_category.id,
             'rate': 1.0,
             'factor': 1.0,
             })
-        category_id = self.category.create({'name': 'ProdCategoryTest'})
-        product_id = self.product.create({
+        category = self.category.create({'name': 'ProdCategoryTest'})
+        product = self.product.create({
                 'name': 'ProductTest',
-                'default_uom': uom_id,
-                'category': category_id,
+                'default_uom': uom.id,
+                'category': category.id,
                 'account_category': True,
                 'list_price': Decimal(0),
                 'cost_price': Decimal(0),
                 })
-        company_id, = self.company.search([('name', '=', 'B2CK')])
-        self.user.write(USER, {
-            'main_company': company_id,
-            'company': company_id,
+        company, = self.company.search([('name', '=', 'B2CK')])
+        self.user.write([self.user(USER)], {
+            'main_company': company.id,
+            'company': company.id,
             })
-        receivable_id, = self.account.search([
+        receivable, = self.account.search([
             ('kind', '=', 'receivable'),
-            ('company', '=', company_id),
+            ('company', '=', company.id),
             ])
-        payable_id, = self.account.search([
+        payable, = self.account.search([
             ('kind', '=', 'payable'),
-            ('company', '=', company_id),
+            ('company', '=', company.id),
             ])
-        supplier_id = self.party.create({
+        supplier = self.party.create({
             'name': 'supplier',
-            'account_receivable': receivable_id,
-            'account_payable': payable_id,
+            'account_receivable': receivable.id,
+            'account_payable': payable.id,
             })
-        product_supplier_id = self.product_supplier.create({
-            'product': product_id,
-            'company': company_id,
-            'party': supplier_id,
+        product_supplier = self.product_supplier.create({
+            'product': product.id,
+            'company': company.id,
+            'party': supplier.id,
             'delivery_time': delivery_time,
             })
         self.product_supplier_day.create({
-            'product_supplier': product_supplier_id,
+            'product_supplier': product_supplier.id,
             'weekday': str(weekday),
             })
-        return product_supplier_id
+        return product_supplier
 
 
 def suite():
diff -r d163ac00ea95 trytond/modules/stock_supply_forecast/__init__.py
--- a/trytond/modules/stock_supply_forecast/__init__.py	Fri Jun 08 19:34:35 2012 +0200
+++ b/trytond/modules/stock_supply_forecast/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,4 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of this
 #repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from .purchase_request import *
+
+
+def register():
+    Pool.register(
+        PurchaseRequest,
+        module='stock_supply_forecast', type_='model')
diff -r d163ac00ea95 trytond/modules/stock_supply_forecast/purchase_request.py
--- a/trytond/modules/stock_supply_forecast/purchase_request.py	Fri Jun 08 19:34:35 2012 +0200
+++ b/trytond/modules/stock_supply_forecast/purchase_request.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,24 +1,26 @@
 #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 Model
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['PurchaseRequest']
+__metaclass__ = PoolMeta
 
-class PurchaseRequest(Model):
-    _name = 'purchase.request'
 
-    def generate_requests(self):
-        forecast_obj = Pool().get('stock.forecast')
-        date_obj = Pool().get('ir.date')
+class PurchaseRequest:
+    __name__ = 'purchase.request'
 
-        today = date_obj.today()
+    @classmethod
+    def generate_requests(cls):
+        pool = Pool()
+        Forecast = pool.get('stock.forecast')
+        Date = pool.get('ir.date')
 
-        forecast_ids = forecast_obj.search([
+        today = Date.today()
+
+        forecasts = Forecast.search([
                 ('to_date', '>=', today),
                 ('state', '=', 'done'),
                 ])
-        forecast_obj.create_moves(forecast_ids)
-        super(PurchaseRequest, self).generate_requests()
-        forecast_obj.delete_moves(forecast_ids)
-
-PurchaseRequest()
+        Forecast.create_moves(forecasts)
+        super(PurchaseRequest, cls).generate_requests()
+        Forecast.delete_moves(forecasts)
diff -r 3ac6556ec8e0 trytond/modules/stock_supply_production/__init__.py
--- a/trytond/modules/stock_supply_production/__init__.py	Mon Sep 24 18:27:07 2012 +0200
+++ b/trytond/modules/stock_supply_production/__init__.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,6 +1,20 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
+from trytond.pool import Pool
 from stock import *
 from production import *
 from product import *
+
+
+def register():
+    Pool.register(
+        OrderPoint,
+        Configuration,
+        Production,
+        CreateProductionRequestStart,
+        Product,
+        module='stock_supply_production', type_='model')
+    Pool.register(
+        CreateProductionRequest,
+        module='stock_supply_production', type_='wizard')
diff -r 3ac6556ec8e0 trytond/modules/stock_supply_production/product.py
--- a/trytond/modules/stock_supply_production/product.py	Mon Sep 24 18:27:07 2012 +0200
+++ b/trytond/modules/stock_supply_production/product.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,17 +1,17 @@
 #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 Model
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 
+__all__ = ['Product']
+__metaclass__ = PoolMeta
 
-class Product(Model):
-    _name = 'product.product'
 
-    def get_supply_period(self, product):
+class Product:
+    __name__ = 'product.product'
+
+    def get_supply_period(self):
         'Return the supply period for the product'
         pool = Pool()
-        configuration_obj = pool.get('production.configuration')
-        return int(configuration_obj.browse(1).supply_period or 0)
-
-Product()
+        Configuration = pool.get('production.configuration')
+        return int(Configuration(1).supply_period or 0)
diff -r 3ac6556ec8e0 trytond/modules/stock_supply_production/production.py
--- a/trytond/modules/stock_supply_production/production.py	Mon Sep 24 18:27:07 2012 +0200
+++ b/trytond/modules/stock_supply_production/production.py	Mon Sep 24 20:36:43 2012 +0200
@@ -3,28 +3,31 @@
 import datetime
 from decimal import Decimal
 
-from trytond.model import Model, ModelView, fields
-from trytond.pool import Pool
+from trytond.model import ModelView, fields
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 from trytond.wizard import Wizard, StateView, StateAction, Button
 
+__all__ = ['Configuration', 'Production',
+    'CreateProductionRequestStart', 'CreateProductionRequest']
+__metaclass__ = PoolMeta
 
-class Configuration(Model):
-    _name = 'production.configuration'
 
+class Configuration:
+    __name__ = 'production.configuration'
     supply_period = fields.Property(fields.Numeric('Supply Period',
             digits=(16, 0), help='In number of days', required=True))
 
-    def default_supply_period(self):
+    @staticmethod
+    def default_supply_period():
         return Decimal(0)
 
-Configuration()
 
+class Production:
+    __name__ = 'production'
 
-class Production(Model):
-    _name = 'production'
-
-    def generate_requests(self, clean=True):
+    @classmethod
+    def generate_requests(cls, clean=True):
         """
         For each product compute the production request that must be created
         today to meet product outputs.
@@ -32,97 +35,98 @@
         If clean is set, it will remove all previous requests.
         """
         pool = Pool()
-        order_point_obj = pool.get('stock.order_point')
-        product_obj = pool.get('product.product')
-        location_obj = pool.get('stock.location')
-        date_obj = pool.get('ir.date')
-        user_obj = pool.get('res.user')
-        company = user_obj.browse(Transaction().user).company
+        OrderPoint = pool.get('stock.order_point')
+        Product = pool.get('product.product')
+        Location = pool.get('stock.location')
+        Date = pool.get('ir.date')
+        User = pool.get('res.user')
+        company = User(Transaction().user).company
 
         if clean:
-            req_ids = self.search([
+            reqs = cls.search([
                     ('state', '=', 'request'),
                     ])
-            self.delete(req_ids)
+            cls.delete(reqs)
 
         # fetch warehouse
-        warehouse_ids = location_obj.search([
+        warehouses = Location.search([
                 ('type', '=', 'warehouse'),
                 ])
-        warehouses = location_obj.browse(warehouse_ids)
+        warehouse_ids = [w.id for w in warehouses]
         # fetch order points
-        order_point_ids = order_point_obj.search([
+        order_points = OrderPoint.search([
                 ('type', '=', 'production'),
                 ])
         # index them by product
         product2ops = {}
-        for order_point in order_point_obj.browse(order_point_ids):
+        for order_point in order_points:
             product2ops[
                 (order_point.warehouse_location.id, order_point.product.id)
                 ] = order_point
 
         # fetch goods
-        product_ids = product_obj.search([
+        products = Product.search([
                 ('type', '=', 'goods'),
                 ('consumable', '=', False),
                 ('purchasable', '=', False),
                 ])
-        products = product_obj.browse(product_ids)
         # order product by supply period
-        products_period = sorted([(product_obj.get_supply_period(p), p)
-            for p in products])
+        products_period = sorted([(p.get_supply_period(), p)
+                for p in products])
 
         # compute requests
         cursor = Transaction().cursor
-        today = date_obj.today()
+        today = Date.today()
         requests = []
         for i in range(0, len(products), cursor.IN_MAX):
             product_ids = [p.id for p in products[i:i + cursor.IN_MAX]]
             with Transaction().set_context(forecast=True,
                     stock_date_end=today):
-                pbl = product_obj.products_by_location(warehouse_ids,
+                pbl = Product.products_by_location(warehouse_ids,
                     product_ids, with_childs=True, skip_zero=False)
             for warehouse in warehouses:
                 quantities = dict((x, pbl.pop((warehouse.id, x)))
                     for x in product_ids)
-                shortages = self.get_shortage(warehouse.id, product_ids, today,
+                shortages = cls.get_shortage(warehouse.id, product_ids, today,
                     quantities, products_period, product2ops)
 
                 for product in products[i:i + cursor.IN_MAX]:
                     for date, quantity in shortages[product.id]:
-                        req_values = self.compute_request(product, warehouse,
+                        req = cls.compute_request(product, warehouse,
                             quantity, date, company)
-                        req_id = self.create(req_values)
-                        self.set_moves(self.browse(req_id))
-                        requests.append(req_id)
+                        req.save()
+                        req.set_moves()
+                        requests.append(req)
         if requests:
-            self.generate_requests(clean=False)
+            cls.generate_requests(clean=False)
         return requests
 
-    def compute_request(self, product, warehouse, quantity, date, company):
+    @classmethod
+    def compute_request(cls, product, warehouse, quantity, date, company):
         """
         Return the value of the production request.
         """
         pool = Pool()
-        date_obj = pool.get('ir.date')
-        today = date_obj.today()
+        Date = pool.get('ir.date')
+        today = Date.today()
         if date <= today:
             date = today
         else:
             date -= datetime.timedelta(1)
-        return {
-            'planned_date': date,
-            'company': company.id,
-            'warehouse': warehouse.id,
-            'location': warehouse.production_location.id,
-            'product': product.id,
-            'bom': product.boms[0].bom.id if product.boms else None,
-            'uom': product.default_uom.id,
-            'quantity': quantity,
-            'state': 'request',
-            }
+        return cls(
+            planned_date=date,
+            company=company,
+            warehouse=warehouse,
+            location=warehouse.production_location,
+            product=product,
+            bom=product.boms[0].bom if product.boms else None,
+            uom=product.default_uom,
+            quantity=quantity,
+            state='request',
+            )
 
-    def get_shortage(self, location_id, product_ids, date, quantities,
+    @classmethod
+    def get_shortage(cls, location_id, product_ids, date, quantities,
             products_period, order_points):
         """
         Return for each product a list of dates where the stock quantity is
@@ -137,7 +141,7 @@
         order_points is a dictionary that links products to order points.
         """
         pool = Pool()
-        product_obj = pool.get('product.product')
+        Product = pool.get('product.product')
 
         shortages = {}
 
@@ -170,7 +174,7 @@
             # Update current quantities
             with Transaction().set_context(stock_date_start=current_date,
                     stock_date_end=current_date):
-                pbl = product_obj.products_by_location([location_id],
+                pbl = Product.products_by_location([location_id],
                     product_ids, with_childs=True, skip_zero=False)
             for key, qty in pbl.iteritems():
                 _, product_id = key
@@ -185,21 +189,15 @@
 
         return shortages
 
-Production()
-
 
 class CreateProductionRequestStart(ModelView):
     'Create Production Request'
-    _name = 'production.create_request.start'
-    _description = __doc__
-
-CreateProductionRequestStart()
+    __name__ = 'production.create_request.start'
 
 
 class CreateProductionRequest(Wizard):
     'Create Production Request'
-    _name = 'production.create_request'
-
+    __name__ = 'production.create_request'
     start = StateView('production.create_request.start',
         'stock_supply_production.production_create_request_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -207,13 +205,11 @@
             ])
     create_ = StateAction('production.act_production_request')
 
-    def do_create_(self, session, action):
+    def do_create_(self, action):
         pool = Pool()
-        production_obj = pool.get('production')
-        production_obj.generate_requests()
+        Production = pool.get('production')
+        Production.generate_requests()
         return action, {}
 
-    def transition_create_(self, session):
+    def transition_create_(self):
         return 'end'
-
-CreateProductionRequest()
diff -r 3ac6556ec8e0 trytond/modules/stock_supply_production/stock.py
--- a/trytond/modules/stock_supply_production/stock.py	Mon Sep 24 18:27:07 2012 +0200
+++ b/trytond/modules/stock_supply_production/stock.py	Mon Sep 24 20:36:43 2012 +0200
@@ -1,46 +1,39 @@
 #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 copy
+from trytond.pyson import Eval
+from trytond.pool import PoolMeta
 
-from trytond.model import Model
-from trytond.pyson import Eval
+__all__ = ['OrderPoint']
+__metaclass__ = PoolMeta
 
 
-class OrderPoint(Model):
-    _name = 'stock.order_point'
+class OrderPoint:
+    __name__ = 'stock.order_point'
 
-    def __init__(self):
-        super(OrderPoint, self).__init__()
+    @classmethod
+    def __setup__(cls):
+        super(OrderPoint, cls).__setup__()
 
-        self.warehouse_location = copy.copy(self.warehouse_location)
-        states = copy.copy(self.warehouse_location.states)
-        states['invisible'] = (states['invisible']
-            & (Eval('type') != 'production'))
-        states['required'] = (states['required']
-            | (Eval('type') == 'production'))
-        self.warehouse_location.states = states
+        cls.warehouse_location.states['invisible'] &= (
+            Eval('type') != 'production')
+        cls.warehouse_location.states['required'] |= (
+            Eval('type') == 'production')
 
         option = ('production', 'Production')
-        if option not in self.type.selection:
-            self.type = copy.copy(self.type)
-            self.type.selection = copy.copy(self.type.selection)
-            self.type.selection.append(option)
+        if option not in cls.type.selection:
+            cls.type.selection.append(option)
 
-        self._reset_columns()
-
-    def _type2field(self, type=None):
+    @classmethod
+    def _type2field(cls, type=None):
         if type == 'production':
             return 'warehouse_location'
-        result = super(OrderPoint, self)._type2field(type=type)
+        result = super(OrderPoint, cls)._type2field(type=type)
         if type == None:
             result['production'] = 'warehouse_location'
         return result
 
-    def get_location(self, ids, name):
-        locations = super(OrderPoint, self).get_location(ids, name)
-        for op in self.browse(ids):
-            if op.type == 'production':
-                locations[op.id] = op.warehouse_location.id
-        return locations
-
-OrderPoint()
+    def get_location(self, name):
+        location = super(OrderPoint, self).get_location(name)
+        if self.type == 'production':
+            return self.warehouse_location.id
+        return location
diff -r 3d07cd37899c trytond/modules/timesheet/__init__.py
--- a/trytond/modules/timesheet/__init__.py	Fri Sep 21 15:41:53 2012 +0200
+++ b/trytond/modules/timesheet/__init__.py	Mon Sep 24 20:36:44 2012 +0200
@@ -1,5 +1,26 @@
 #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.pool import Pool
 from .work import *
 from .line import *
+
+
+def register():
+    Pool.register(
+        Work,
+        OpenWorkStart,
+        Line,
+        EnterLinesStart,
+        HoursEmployee,
+        OpenHoursEmployeeStart,
+        HoursEmployeeWeekly,
+        HoursEmployeeMonthly,
+        module='timesheet', type_='model')
+    Pool.register(
+        OpenWork,
+        OpenWork2,
+        OpenWorkGraph,
+        EnterLines,
+        OpenHoursEmployee,
+        module='timesheet', type_='wizard')
diff -r 3d07cd37899c trytond/modules/timesheet/line.py
--- a/trytond/modules/timesheet/line.py	Fri Sep 21 15:41:53 2012 +0200
+++ b/trytond/modules/timesheet/line.py	Mon Sep 24 20:36:44 2012 +0200
@@ -7,12 +7,15 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Line', 'EnterLinesStart', 'EnterLines',
+    'HoursEmployee',
+    'OpenHoursEmployeeStart', 'OpenHoursEmployee',
+    'HoursEmployeeWeekly', 'HoursEmployeeMonthly']
+
 
 class Line(ModelSQL, ModelView):
     'Timesheet Line'
-    _name = 'timesheet.line'
-    _description = __doc__
-
+    __name__ = 'timesheet.line'
     employee = fields.Many2One('company.employee', 'Employee', required=True,
         select=True, domain=[
             ('company', '=', Get(Eval('context', {}), 'company')),
@@ -25,63 +28,62 @@
             ])
     description = fields.Char('Description')
 
-    def __init__(self):
-        super(Line, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Line, cls).__setup__()
+        cls._sql_constraints += [
             ('check_move_hours_pos',
              'CHECK(hours >= 0.0)', 'Hours field must be positive'),
             ]
 
-    def default_employee(self):
-        user_obj = Pool().get('res.user')
+    @staticmethod
+    def default_employee():
+        User = Pool().get('res.user')
         employee_id = None
         if Transaction().context.get('employee'):
             employee_id = Transaction().context['employee']
         else:
-            user = user_obj.browse(Transaction().user)
+            user = User(Transaction().user)
             if user.employee:
                 employee_id = user.employee.id
         if employee_id:
             return employee_id
 
-    def default_date(self):
-        date_obj = Pool().get('ir.date')
+    @staticmethod
+    def default_date():
+        Date_ = Pool().get('ir.date')
+        return Transaction().context.get('date') or Date_.today()
 
-        return Transaction().context.get('date') or date_obj.today()
-
-    def view_header_get(self, value, view_type='form'):
+    @classmethod
+    def view_header_get(cls, value, view_type='form'):
         if not Transaction().context.get('employee'):
             return value
-        employee_obj = Pool().get('company.employee')
-        employee = employee_obj.browse(Transaction().context['employee'])
+        Employee = Pool().get('company.employee')
+        employee = Employee(Transaction().context['employee'])
         return value + " (" + employee.name + ")"
 
-Line()
-
 
 class EnterLinesStart(ModelView):
     'Enter Lines'
-    _name = 'timesheet.line.enter.start'
-    _description = __doc__
+    __name__ = 'timesheet.line.enter.start'
     employee = fields.Many2One('company.employee', 'Employee', required=True,
             domain=[('company', '=', Get(Eval('context', {}), 'company'))])
     date = fields.Date('Date', required=True)
 
-    def default_employee(self):
-        line_obj = Pool().get('timesheet.line')
-        return line_obj.default_employee()
+    @staticmethod
+    def default_employee():
+        Line = Pool().get('timesheet.line')
+        return Line.default_employee()
 
-    def default_date(self):
-        line_obj = Pool().get('timesheet.line')
-        return line_obj.default_date()
-
-EnterLinesStart()
+    @staticmethod
+    def default_date():
+        Line = Pool().get('timesheet.line')
+        return Line.default_date()
 
 
 class EnterLines(Wizard):
     'Enter Lines'
-    _name = 'timesheet.line.enter'
-
+    __name__ = 'timesheet.line.enter'
     start = StateView('timesheet.line.enter.start',
         'timesheet.line_enter_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -89,35 +91,32 @@
             ])
     enter = StateAction('timesheet.act_line_form')
 
-    def do_enter(self, session, action):
-        date = session.start.date
+    def do_enter(self, action):
+        date = self.start.date
         date = Date(date.year, date.month, date.day)
         action['pyson_domain'] = PYSONEncoder().encode([
-                ('employee', '=', session.start.employee.id),
+                ('employee', '=', self.start.employee.id),
                 ('date', '=', date),
                 ])
         action['pyson_context'] = PYSONEncoder().encode({
-                'employee': session.start.employee.id,
+                'employee': self.start.employee.id,
                 'date': date,
                 })
-        action['name'] += " - " + session.start.employee.rec_name
+        action['name'] += " - " + self.start.employee.rec_name
         return action, {}
 
-    def transition_enter(self, session):
+    def transition_enter(self):
         return 'end'
 
-EnterLines()
-
 
 class HoursEmployee(ModelSQL, ModelView):
     'Hours per Employee'
-    _name = 'timesheet.hours_employee'
-    _description = __doc__
-
+    __name__ = 'timesheet.hours_employee'
     employee = fields.Many2One('company.employee', 'Employee', select=True)
     hours = fields.Float('Hours', digits=(16, 2))
 
-    def table_query(self):
+    @staticmethod
+    def table_query():
         clause = ' '
         args = [True]
         if Transaction().context.get('start_date'):
@@ -138,23 +137,17 @@
                 + clause + \
                 'GROUP BY employee', args)
 
-HoursEmployee()
-
 
 class OpenHoursEmployeeStart(ModelView):
     'Open Hours per Employee'
-    _name = 'timesheet.hours_employee.open.start'
-    _description = __doc__
+    __name__ = 'timesheet.hours_employee.open.start'
     start_date = fields.Date('Start Date')
     end_date = fields.Date('End Date')
 
-OpenHoursEmployeeStart()
-
 
 class OpenHoursEmployee(Wizard):
     'Open Hours per Employee'
-    _name = 'timesheet.hours_employee.open'
-
+    __name__ = 'timesheet.hours_employee.open'
     start = StateView('timesheet.hours_employee.open.start',
         'timesheet.hours_employee_open_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -162,37 +155,35 @@
             ])
     open_ = StateAction('timesheet.act_hours_employee_form')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         action['pyson_context'] = PYSONEncoder().encode({
-                'start_date': session.start.start_date,
-                'end_date': session.start.end_date,
+                'start_date': self.start.start_date,
+                'end_date': self.start.end_date,
                 })
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
 
-OpenHoursEmployee()
-
 
 class HoursEmployeeWeekly(ModelSQL, ModelView):
     'Hours per Employee per Week'
-    _name = 'timesheet.hours_employee_weekly'
-    _description = __doc__
-
+    __name__ = 'timesheet.hours_employee_weekly'
     year = fields.Char('Year', select=True)
     week = fields.Integer('Week', select=True)
     employee = fields.Many2One('company.employee', 'Employee', select=True)
     hours = fields.Float('Hours', digits=(16, 2), select=True)
 
-    def __init__(self):
-        super(HoursEmployeeWeekly, self).__init__()
-        self._order.insert(0, ('year', 'DESC'))
-        self._order.insert(1, ('week', 'DESC'))
-        self._order.insert(2, ('employee', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(HoursEmployeeWeekly, cls).__setup__()
+        cls._order.insert(0, ('year', 'DESC'))
+        cls._order.insert(1, ('week', 'DESC'))
+        cls._order.insert(2, ('employee', 'ASC'))
 
-    def table_query(self):
-        type_name = FIELDS[self.year._type].sql_type(self.year)[0]
+    @classmethod
+    def table_query(cls):
+        type_name = FIELDS[cls.year._type].sql_type(cls.year)[0]
         return ('SELECT id, create_uid, create_date, write_uid, write_date, ' \
                     'CAST(year AS ' + type_name + ') AS year, week, ' \
                     'employee, hours ' \
@@ -208,29 +199,27 @@
                         'EXTRACT(WEEK FROM date) AS week, employee, ' \
                         'SUM(COALESCE(hours, 0)) AS hours ' \
                     'FROM timesheet_line ' \
-                    'GROUP BY year, week, employee) AS ' + self._table, [])
-
-HoursEmployeeWeekly()
+                    'GROUP BY year, week, employee) AS ' + cls._table, [])
 
 
 class HoursEmployeeMonthly(ModelSQL, ModelView):
     'Hours per Employee per Month'
-    _name = 'timesheet.hours_employee_monthly'
-    _description = __doc__
-
+    __name__ = 'timesheet.hours_employee_monthly'
     year = fields.Char('Year', select=True)
     month = fields.Integer('Month', select=True)
     employee = fields.Many2One('company.employee', 'Employee', select=True)
     hours = fields.Float('Hours', digits=(16, 2), select=True)
 
-    def __init__(self):
-        super(HoursEmployeeMonthly, self).__init__()
-        self._order.insert(0, ('year', 'DESC'))
-        self._order.insert(1, ('month', 'DESC'))
-        self._order.insert(2, ('employee', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(HoursEmployeeMonthly, cls).__setup__()
+        cls._order.insert(0, ('year', 'DESC'))
+        cls._order.insert(1, ('month', 'DESC'))
+        cls._order.insert(2, ('employee', 'ASC'))
 
-    def table_query(self):
-        type_name = FIELDS[self.year._type].sql_type(self.year)[0]
+    @classmethod
+    def table_query(cls):
+        type_name = FIELDS[cls.year._type].sql_type(cls.year)[0]
         return ('SELECT id, create_uid, create_date, write_uid, write_date, ' \
                     'CAST(year AS ' + type_name + ') AS year, month, ' \
                     'employee, hours ' \
@@ -246,6 +235,4 @@
                         'EXTRACT(MONTH FROM date) AS month, employee, ' \
                         'SUM(COALESCE(hours, 0)) AS hours ' \
                     'FROM timesheet_line ' \
-                    'GROUP BY year, month, employee) AS ' + self._table, [])
-
-HoursEmployeeMonthly()
+                    'GROUP BY year, month, employee) AS ' + cls._table, [])
diff -r 3d07cd37899c trytond/modules/timesheet/work.py
--- a/trytond/modules/timesheet/work.py	Fri Sep 21 15:41:53 2012 +0200
+++ b/trytond/modules/timesheet/work.py	Mon Sep 24 20:36:44 2012 +0200
@@ -6,12 +6,12 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = ['Work', 'OpenWorkStart', 'OpenWork', 'OpenWork2', 'OpenWorkGraph']
+
 
 class Work(ModelSQL, ModelView):
     'Work'
-    _name = 'timesheet.work'
-    _description = __doc__
-
+    __name__ = 'timesheet.work'
     name = fields.Char('Name', required=True)
     active = fields.Boolean('Active')
     parent = fields.Many2One('timesheet.work', 'Parent', left="left",
@@ -40,48 +40,52 @@
             'readonly': Not(Bool(Eval('active'))),
             })
 
-    def __init__(self):
-        super(Work, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Work, cls).__setup__()
+        cls._constraints += [
             ('check_recursion', 'recursive_works'),
             ('check_parent_company', 'parent_company'),
-        ]
-        self._error_messages.update({
-            'recursive_works': 'You can not create recursive works!',
-            'parent_company': 'Every work must be in the same company '\
-                'as it\'s parent work!',
-        })
+            ]
+        cls._error_messages.update({
+                'recursive_works': 'You can not create recursive works!',
+                'parent_company': 'Every work must be in the same company '\
+                    'as it\'s parent work!',
+                })
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_left(self):
+    @staticmethod
+    def default_left():
         return 0
 
-    def default_right(self):
+    @staticmethod
+    def default_right():
         return 0
 
-    def default_timesheet_available(self):
+    @staticmethod
+    def default_timesheet_available():
         return True
 
-    def default_company(self):
+    @staticmethod
+    def default_company():
         return Transaction().context.get('company')
 
-    def check_parent_company(self, ids):
-        for work in self.browse(ids):
-            if not work.parent:
-                continue
-            if work.parent.company.id != work.company.id:
-                return False
-        return True
+    def check_parent_company(self):
+        if not self.parent:
+            return True
+        return self.parent.company == self.company
 
-    def _tree_qty(self, hours_by_wt, children, ids, to_compute):
+    @classmethod
+    def _tree_qty(cls, hours_by_wt, children, ids, to_compute):
         res = 0
         for h in ids:
             if (not children.get(h)) or (not to_compute[h]):
                 res += hours_by_wt.setdefault(h, 0)
             else:
-                sub_qty = self._tree_qty(
+                sub_qty = cls._tree_qty(
                     hours_by_wt, children, children[h], to_compute)
                 hours_by_wt.setdefault(h, 0)
                 hours_by_wt[h] += sub_qty
@@ -89,10 +93,13 @@
                 to_compute[h] = False
         return res
 
-    def get_hours(self, ids, name):
-        all_ids = self.search([
+    @classmethod
+    def get_hours(cls, works, name):
+        ids = [w.id for w in works]
+        all_works = cls.search([
                 ('parent', 'child_of', ids),
                 ])
+        all_ids = [w.id for w in all_works]
         # force inactive ids to be in all_ids
         all_ids = list(set(all_ids + ids))
         clause = "SELECT work, sum(hours) FROM timesheet_line "\
@@ -113,66 +120,53 @@
         hours_by_wt = dict((i[0], i[1]) for i in
             Transaction().cursor.fetchall())
         to_compute = dict.fromkeys(all_ids, True)
-        works = self.browse(all_ids)
+        works = cls.browse(all_ids)
         children = {}
         for work in works:
             if work.parent:
                 children.setdefault(work.parent.id, []).append(work.id)
-        self._tree_qty(hours_by_wt, children, ids, to_compute)
+        cls._tree_qty(hours_by_wt, children, ids, to_compute)
         return hours_by_wt
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.get_rec_name(name) + '\\' + self.name
+        else:
+            return self.name
 
-        def _name(work):
-            if work.parent:
-                return _name(work.parent) + '\\' + work.name
-            else:
-                return work.name
-        for work in self.browse(ids):
-            res[work.id] = _name(work)
-        return res
-
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, works, default=None):
         if default is None:
             default = {}
         default = default.copy()
         if 'timesheet_lines' not in default:
             default['timesheet_lines'] = None
-        return super(Work, self).copy(ids, default=default)
+        return super(Work, cls).copy(works, default=default)
 
-    def write(self, ids, vals):
-        child_ids = None
+    @classmethod
+    def write(cls, works, vals):
+        childs = None
         if not vals.get('active', True):
-            child_ids = self.search([
-                ('parent', 'child_of', ids),
-                ])
-        res = super(Work, self).write(ids, vals)
-        if child_ids:
-            self.write(child_ids, {
-                'active': False,
-                })
-        return res
-
-Work()
+            childs = cls.search([
+                    ('parent', 'child_of', [w.id for w in works]),
+                    ])
+        super(Work, cls).write(works, vals)
+        if childs:
+            cls.write(childs, {
+                    'active': False,
+                    })
 
 
 class OpenWorkStart(ModelView):
     'Open Work'
-    _name = 'timesheet.work.open.start'
-    _description = __doc__
+    __name__ = 'timesheet.work.open.start'
     from_date = fields.Date('From Date')
     to_date = fields.Date('To Date')
 
-OpenWorkStart()
-
 
 class OpenWork(Wizard):
     'Open Work'
-    _name = 'timesheet.work.open'
-
+    __name__ = 'timesheet.work.open'
     start = StateView('timesheet.work.open.start',
         'timesheet.work_open_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -180,39 +174,31 @@
             ])
     open_ = StateAction('timesheet.act_work_hours_board')
 
-    def do_open_(self, session, action):
+    def do_open_(self, action):
         action['pyson_context'] = PYSONEncoder().encode({
-                'from_date': session.start.from_date,
-                'to_date': session.start.to_date,
+                'from_date': self.start.from_date,
+                'to_date': self.start.to_date,
                 })
         return action, {}
 
-    def transition_open_(self, session):
+    def transition_open_(self):
         return 'end'
 
-OpenWork()
-
 
 class OpenWork2(OpenWork):
-    _name = 'timesheet.work.open2'
-
+    __name__ = 'timesheet.work.open2'
     open_ = StateAction('timesheet.act_work_form2')
 
-OpenWork2()
-
 
 class OpenWorkGraph(Wizard):
-    _name = 'timesheet.work.open_graph'
+    __name__ = 'timesheet.work.open_graph'
     start_state = 'open_'
     open_ = StateAction('timesheet.act_work_form3')
 
-    def do_open_(self, session, action):
-        pool = Pool()
-        work_obj = pool.get('timesheet.work')
+    def do_open_(self, action):
+        Work = Pool().get('timesheet.work')
 
         if 'active_id' in Transaction().context:
-            work = work_obj.browse(Transaction().context['active_id'])
+            work = Work(Transaction().context['active_id'])
             action['name'] = action['name'] + ' - ' + work.rec_name
         return action, {}
-
-OpenWorkGraph()
