# HG changeset patch
# User Matthieu Gautier <matthieu.gautier@mgautier.fr>
# Date 1429114256 -7200
#      Wed Apr 15 18:10:56 2015 +0200
# Branch odoo8
# Node ID 459109cdadb33779e8e16af495e4a6d2302e952b
# Parent  e680f59ab07db9a142e6729f71a773cb2d224a5e
Port to odoo v8

Main changes are :
- Change fields declaration
- Update metaclass to add field to nmspc instead of _columns
- Add v8's version of compute/reverse/search function of
    disable_per_company field

diff --git a/MetaAnalytic.py b/MetaAnalytic.py
--- a/MetaAnalytic.py
+++ b/MetaAnalytic.py
@@ -1,5 +1,5 @@
 from openerp import SUPERUSER_ID
-from openerp.osv import fields
+from openerp import fields, api
 from openerp.tools import config
 
 from openerp.addons.oemetasl import OEMetaSL
@@ -30,7 +30,7 @@
 
     Notes:
     * This metaclass may directly modify attributes that are used by OpenERP,
-    specifically _columns and _inherits.
+    specifically _inherits and the fields of the class.
     * New superclasses are used to define or override methods, in order to
     avoid interacting with OEMetaSL or the model's own method (re)definitions.
     """
@@ -41,11 +41,6 @@
         para = nmspc.get('_para_analytic', {})
         dimension = nmspc.get('_dimension', {})
 
-        columns = nmspc.get('_columns', None)
-        if columns is None:
-            columns = {}
-            nmspc['_columns'] = columns
-
         defaults = nmspc.get('_defaults', None)
         if defaults is None:
             defaults = {}
@@ -58,13 +53,13 @@
         # Analytic fields should be defined in the _analytic attribute.
         if analytic or para:
             bases = cls._setup_analytic_fields(
-                analytic, para, columns, defaults, orm_name, name, bases, nmspc
+                analytic, para, defaults, orm_name, name, bases, nmspc
             )
 
         # The bound dimension should be defined in the _dimension attribute.
         if dimension:
             bases = cls._setup_bound_dimension(
-                dimension, columns, defaults, orm_name, name, bases, nmspc
+                dimension, defaults, orm_name, name, bases, nmspc
             )
 
         return super(MetaAnalytic, cls).__new__(cls, name, bases, nmspc)
@@ -74,7 +69,7 @@
 
     @classmethod
     def _setup_analytic_fields(
-        cls, analytic, para, columns, defaults, orm_name, name, bases, nmspc
+        cls, analytic, para, defaults, orm_name, name, bases, nmspc
     ):
         """Generate analytic and para-analytic fields on the model."""
 
@@ -86,17 +81,16 @@
 
         # Create a field that will be used for replacement in the view
         if analytic:
-            columns['analytic_dimensions'] = fields.function(
-                lambda self, cr, uid, ids, *a: {i: '' for i in ids},
-                string=u"Analytic Dimensions",
-                readonly=True,
-                store=False,
+            nmspc['analytic_dimensions'] = fields.Char(
+                string  = u"Analytic Dimensions",
+                compute = lambda self, cr, uid, ids, *a: {i: '' for i in ids},
+                readonly=True
             )
 
         col_pattern = '{pre}{n}_{suf}'
         size = int(config.get_misc('analytic', 'analytic_size', 5))
 
-        # Generate the fields directly into the _columns attribute.
+        # Generate the fields directly into the nmspc.
         all_analytic = []
 
         for prefix, model_name in analytic.iteritems():
@@ -106,7 +100,7 @@
             for n in xrange(1, size + 1):
                 col_name = col_pattern.format(pre=prefix, n=n, suf='id')
                 domain_field = 'nd_id.ns{n}_id.model_name'.format(n=n)
-                columns[col_name] = fields.many2one(
+                nmspc[col_name] = fields.Many2one(
                     'analytic.code',
                     "Generated Analytic Field",
                     domain=[
@@ -130,7 +124,7 @@
             kwargs = value['kwargs']
             for n in xrange(1, size + 1):
                 col_name = col_pattern.format(pre=prefix, n=n, suf=suffix)
-                columns[col_name] = field_type(*args, **kwargs)
+                nmspc[col_name] = field_type(*args, **kwargs)
                 if 'default' in value:
                     defaults[col_name] = value['default']
 
@@ -186,7 +180,7 @@
 
     @classmethod
     def _setup_bound_dimension(
-        cls, dimension, columns, defaults, orm_name, name, bases, nmspc
+        cls, dimension, defaults, orm_name, name, bases, nmspc
     ):
         """Bind a dimension to the model, creating a code for each record."""
 
@@ -252,7 +246,7 @@
         use_inherits = dimension.get('use_inherits', None)
         if use_inherits is None:
             use_inherits = not (
-                any(col in columns for col in ('name', 'nd_id'))
+                any(field in nmspc for field in ('name', 'nd_id'))
                 or nmspc.get('_inherits', False)
                 or nmspc.get('_inherit', False)
             )
@@ -271,8 +265,8 @@
             nmspc['_inherits'] = inherits
 
         # Default column for the underlying analytic code.
-        if column not in columns:
-            columns[column] = fields.many2one(
+        if column not in nmspc:
+            nmspc[column] = fields.Many2one(
                 'analytic.code',
                 u"Bound Analytic Code",
                 required=True,
@@ -297,20 +291,12 @@
                 return osv.search(cr, uid, domain, context=context)
 
             for string, model_col, code_col, dtype, req, default in rel_cols:
-                columns[model_col] = fields.related(
-                    column,
-                    code_col,
-                    string=string,
-                    type=dtype,
+                nmspc[model_col] = getattr(fields, dtype)(
+                    string = string,
+                    related = ".".join([column, code_cod]),
                     relation="analytic.code",
                     required=req,
-                    store={
-                        'analytic.code': (
-                            _record_from_code_id,
-                            [code_col],
-                            10
-                        )
-                    }
+                    store=True
                 )
                 if model_col not in defaults:
                     defaults[model_col] = default
diff --git a/analytic_code.py b/analytic_code.py
--- a/analytic_code.py
+++ b/analytic_code.py
@@ -18,11 +18,11 @@
 #
 ##############################################################################
 
-from openerp.osv import fields, osv
+from openerp import models, fields, api
 from openerp.tools import config
 
 
-class analytic_code(osv.Model):
+class analytic_code(models.Model):
     _name = 'analytic.code'
     _description = u"Analytic Code"
 
@@ -31,69 +31,44 @@
     _parent_order = 'name'
     _order = 'parent_left'
 
-    def _read_disabled_per_company(
-        self, cr, uid, ids, field_name, arg, context
-    ):
+    @api.depends('blacklist_ids')
+    def _read_disabled_per_company(self):
         """Mark the code as disabled when it is in the blacklist (depending on
         the current user's company).
         """
 
-        anc_obj = self.pool['analytic.code']
-        user_obj = self.pool['res.users']
-
-        company_id = user_obj.read(
-            cr, uid, [uid], ['company_id'], context=context
-        )[0]['company_id'][0]
-
-        ret = {}
+        company_id = self.env.user.company_id.id
 
-        for anc in anc_obj.browse(cr, uid, ids, context=context):
+        for anc in self:
             blacklist = (company.id for company in anc.blacklist_ids)
-            ret[anc.id] = company_id in blacklist
+            anc.disabled_per_company = company_id in blacklist
 
-        return ret
-
-    def _write_disabled_per_company(
-        self, cr, uid, anc_id, field_name, field_value, arg, context
-    ):
+    def _write_disabled_per_company(self):
         """Update the blacklist depending on the current user's company.
         """
 
-        anc_obj = self.pool['analytic.code']
-        user_obj = self.pool['res.users']
-
-        company_id = user_obj.read(
-            cr, uid, [uid], ['company_id'], context=context
-        )[0]['company_id'][0]
-
-        anc = anc_obj.browse(cr, uid, anc_id, context=context)
-        blacklist = (company.id for company in anc.blacklist_ids)
+        company_id = self.env.user.company_id.id
 
-        to_write = None
-        if field_value and company_id not in blacklist:
-            to_write = [(4, company_id)]  # Link.
-        elif not field_value and company_id in blacklist:
-            to_write = [(3, company_id)]  # Unlink.
-
-        if to_write:
-            anc_obj.write(
-                cr, uid, [anc_id], {'blacklist_ids': to_write}, context=context
-            )
+        for anc in self:
+            blacklist = (company.id for company in anc.blacklist_ids)
+    
+            to_write = None
+            if anc.disabled_per_company and company_id not in blacklist:
+                to_write = [(4, company_id)]  # Link.
+            elif not anc.disabled_per_company and company_id in blacklist:
+                to_write = [(3, company_id)]  # Unlink.
+    
+            if to_write:
+                anc.write({'blacklist_ids': to_write})
 
         return True
 
-    def _search_disabled_per_company(
-        self, cr, uid, model_again, field_name, criterion, context
-    ):
+    def _search_disabled_per_company(self, operator, value):
         """Update the domain to take the blacklist into account (depending on
         the current user's company).
         """
 
-        user_obj = self.pool['res.users']
-
-        company_id = user_obj.read(
-            cr, uid, [uid], ['company_id'], context=context
-        )[0]['company_id'][0]
+        company_id = self.env.user.company_id.id
 
         # We assume the criterion was "disabled_per_company = False".
         dom = [
@@ -101,96 +76,86 @@
             ('blacklist_ids', '=', False),
             ('blacklist_ids', '!=', company_id),  # Not blacklists_ids.id!
         ]
-        if criterion[0][2] is True:
+        if value is True:
             dom = ['!'] + dom
         return dom
 
-    _columns = {
-        'name': fields.char(
-            u"Name",
-            size=128,
-            translate=config.get_misc('analytic', 'translate', False),
-            required=True,
-        ),
-        'nd_id': fields.many2one(
-            'analytic.dimension',
-            string=u"Dimension",
-            ondelete='cascade',
-            required=True,
-        ),
+    name = fields.Char(
+        u"Name",
+        size=128,
+        translate=config.get_misc('analytic', 'translate', False),
+        required=True,
+    )
+    nd_id = fields.Many2one(
+        'analytic.dimension',
+        string=u"Dimension",
+        ondelete='cascade',
+        required=True,
+    )    
 
-        'active': fields.boolean(
-            u"Active",
-            help=(
-                u"Determines whether an analytic code is in the referential."
-            ),
+    active = fields.Boolean(
+        u"Active",
+        help=(
+            u"Determines whether an analytic code is in the referential."
         ),
-        'view_type': fields.boolean(
-            u"View type",
-            help=(
-                u"Determines whether an analytic code is not selectable (but "
-                u"still in the referential)."
-            ),
-        ),
-        'blacklist_ids': fields.many2many(
-            'res.company',
-            'analytic_code_company_rel',
-            'code_id',
-            'company_id',
-            u"Blacklist",
-            help=u"Companies the code is hidden in.",
+        default = lambda *a: True
+    )
+    view_type = fields.Boolean(
+        u"View type",
+        help=(
+            u"Determines whether an analytic code is not selectable (but "
+            u"still in the referential)."
         ),
-        'disabled_per_company': fields.function(
-            _read_disabled_per_company,
-            fnct_inv=_write_disabled_per_company,
-            fnct_search=_search_disabled_per_company,
-            method=True,
-            type='boolean',
-            store=False,  # Not persistent as it depends on the company.
-            string=u"Disabled in my company",
-            help=(
-                u"Determines whether an analytic code is disabled for the "
-                u"current company."
-            ),
-        ),
-
-        'nd_name': fields.related(
-            'nd_id',
-            'name',
-            type='char',
-            string=u"Dimension Name",
-            store=False
+        default = lambda *a: False
+    )
+    blacklist_ids = fields.Many2many(
+        'res.company',
+        'analytic_code_company_rel',
+        'code_id',
+        'company_id',
+        u"Blacklist",
+        help=u"Companies the code is hidden in.",
+    )
+    disabled_per_company = fields.Boolean(
+        string  = u"Disable in my company",
+        compute = _read_disabled_per_company,
+        inverse = _write_disabled_per_company,
+        search  = _search_disabled_per_company,
+        help=(
+            u"Determines whether an analytic code is disabled for the "
+            u"current company."
         ),
-        'description': fields.char(
-            u"Description",
-            size=512,
-            translate=config.get_misc('analytic', 'translate', False),
-        ),
-        'code_parent_id': fields.many2one(
-            'analytic.code',
-            u"Parent Code",
-            select=True,
-            ondelete='restrict',
-        ),
-        'child_ids': fields.one2many(
-            'analytic.code',
-            'code_parent_id',
-            u"Child Codes",
-        ),
-        'parent_left': fields.integer(u"Left parent", select=True),
-        'parent_right': fields.integer(u"Right parent", select=True),
-    }
+        default = lambda *a: False
+    )
 
-    _defaults = {
-        'active': lambda *a: True,
-        'view_type': lambda *a: False,
-        'disabled_per_company': lambda *a: False,
-    }
+    nd_name = fields.Char(
+        related = 'nd_id.name',
+        string=u"Dimension Name",
+        store=False
+    )
+    description = fields.Char(
+        u"Description",
+        size=512,
+        translate=config.get_misc('analytic', 'translate', False),
+    )
+    code_parent_id = fields.Many2one(
+        'analytic.code',
+        u"Parent Code",
+        select=True,
+        ondelete='restrict',
+    )
+    child_ids = fields.One2many(
+        'analytic.code',
+        'code_parent_id',
+        u"Child Codes",
+    )
+    parent_left = fields.Integer(u"Left parent", select=True)
+    parent_right = fields.Integer(u"Right parent", select=True)
 
     _constraints = [
         # very useful base class constraint
         (
-            osv.Model._check_recursion,
+            models.Model._check_recursion,
             u"Error ! You can not create recursive analytic codes.",
             ['parent_id']
         ),
diff --git a/analytic_dimension.py b/analytic_dimension.py
--- a/analytic_dimension.py
+++ b/analytic_dimension.py
@@ -18,7 +18,7 @@
 #
 ##############################################################################
 
-from openerp.osv import fields, osv
+from openerp import models, fields, api
 from openerp.addons.oemetasl import OEMetaSL
 from openerp.tools import config
 
@@ -27,10 +27,9 @@
 
     def __new__(cls, name, bases, nmspc):
 
-        columns = nmspc['_columns']
         size = int(config.get_misc('analytic', 'analytic_size', 5))
         for n in xrange(1, size + 1):
-            columns['ns{}_id'.format(n)] = fields.one2many(
+            nmspc['ns{}_id'.format(n)] = fields.One2many(
                 'analytic.structure',
                 'nd_id',
                 "Generated Subset of Structures",
@@ -40,22 +39,28 @@
         return super(_dimension_meta, cls).__new__(cls, name, bases, nmspc)
 
 
-class analytic_dimension(osv.Model):
+class analytic_dimension(models.Model):
 
     __metaclass__ = _dimension_meta
     _name = 'analytic.dimension'
     _description = u"Analytic Dimension"
 
-    _columns = {
-        'name': fields.char(
-            u"Name",
-            size=128,
-            translate=config.get_misc('analytic', 'translate', False),
-            required=True,
-        ),
-        'nc_ids': fields.one2many('analytic.code', 'nd_id', u"Codes"),
-        'ns_id': fields.one2many('analytic.structure', 'nd_id', u"Structures"),
-    }
+    name = fields.Char(
+        string=u"Name",
+        size=128,
+        translate=config.get_misc('analytic', 'translate', False),
+        required=True,
+    )
+        
+    nc_ids = fields.One2many(
+        comodel_name='analytic.code',
+        inverse_name='nd_id',
+        string=u"Codes")
+        
+    ns_id = fields.One2many(
+        comodel_name='analytic.structure',
+        inverse_name='nd_id',
+        string=u"Structures")
 
     _sql_constraints = [
         ('unique_name', 'unique(name)', u"Name must be unique"),
diff --git a/analytic_structure.py b/analytic_structure.py
--- a/analytic_structure.py
+++ b/analytic_structure.py
@@ -18,7 +18,7 @@
 #
 ##############################################################################
 
-from openerp.osv import fields, osv
+from openerp import fields, models, api
 from openerp.tools import config
 from openerp.tools.translate import _
 import re
@@ -26,12 +26,12 @@
 import json
 
 
-class analytic_structure(osv.Model):
+class analytic_structure(models.Model):
 
     _name = 'analytic.structure'
     _description = u"Analytic Structure"
 
-    def order_selection(self, cr, uid, context=None):
+    def order_selection(self):
         order_selection = getattr(self, '_order_selection', None)
         if order_selection is None:
             size = int(config.get_misc('analytic', 'analytic_size', 5))
@@ -41,9 +41,10 @@
             setattr(self, '_order_selection', order_selection)
         return order_selection
 
-    def _check_unique_ordering_no_company(self, cr, uid, ids, context=None):
+    @api.constrains('company_id', 'model_name', 'ordering')
+    def _check_unique_ordering_no_company(self):
         columns = ['company_id', 'model_name', 'ordering']
-        structures = self.read(cr, uid, ids, columns, context=context)
+        structures = self.read(columns)
         for structure in structures:
             if structure['company_id']:
                 continue  # Already checked by the SQL constraint.
@@ -51,48 +52,33 @@
                 ('model_name', '=', structure['model_name']),
                 ('ordering', '=', structure['ordering']),
             ]
-            count = self.search(cr, uid, domain, count=True, context=context)
+            count = self.search_count(domain)
             if count > 1:
-                return False
-        return True
+                raise exceptions.ValidationError(u"One dimension per Analysis slot per object when the structure is common to all companies.")
 
-    _columns = {
-        'model_name': fields.char(
-            u"Object",
-            size=128,
-            required=True,
-            select='1',
-        ),
-        'nd_id': fields.many2one(
-            'analytic.dimension',
-            u"Related Dimension",
-            ondelete='restrict',
-            required=True,
-            select='1',
-        ),
-        'ordering': fields.selection(
-            order_selection,
-            u"Analysis slot",
-            required=True,
-        ),
-        'company_id': fields.many2one(
-            'res.company',
-            u"Company",
-        ),
-    }
-
-    _defaults = {
-        'company_id': lambda *a: False,
-    }
-
-    _constraints = [
-        (
-            _check_unique_ordering_no_company,
-            u"One dimension per Analysis slot per object when the structure "
-            u"is common to all companies.",
-            ['company_id', 'model_name', 'ordering']
-        )
-    ]
+    model_name = fields.Char(
+        u"Object",
+        size=128,
+        required=True,
+        select='1',
+    )
+    nd_id = fields.Many2one(
+        'analytic.dimension',
+        u"Related Dimension",
+        ondelete='restrict',
+        required=True,
+        select='1',
+    )
+    ordering = fields.Selection(
+        order_selection,
+        u"Analysis slot",
+        required=True,
+    )
+    company_id = fields.Many2one(
+        'res.company',
+        u"Company",
+        default = lambda *a: False
+    )
 
     _sql_constraints = [
         (