# HG changeset patch
# User Jérémie Gavrel <jeremie.gavrel@xcg-consulting.fr>
# Date 1401879923 -7200
#      Wed Jun 04 13:05:23 2014 +0200
# Node ID 740e7dc3bde0f6c375bfce99c7bead17be23e536
# Parent  dffb190652bc0f61c15b064b0ab7023eda71d0e4
Added new options for bound dimensions.

diff --git a/MetaAnalytic.py b/MetaAnalytic.py
--- a/MetaAnalytic.py
+++ b/MetaAnalytic.py
@@ -173,16 +173,31 @@
         if sync_parent is True:
             sync_parent = nmspc.get('_parent_name', 'parent_id')
 
-        # By default, only use inherits if we can be sure there are no 'name'
-        # or 'code_parent_id' field.
+        rel_description = dimension.get('rel_description', tuple())
+        if rel_description is True:
+            rel_description = u"Description"
+        if isinstance(rel_description, basestring):
+            rel_description = (rel_description, 'description')
+
+        rel_active = dimension.get('rel_active', tuple())
+        if rel_active is True:
+            rel_active = u"Active"
+        if isinstance(rel_active, basestring):
+            rel_active = (rel_active, 'active')
+
+        # By default, only use inherits if we can be sure there is no conflict
+        # on the required fields 'name' and 'nd_id'.
+        # There can still be conflicts on analytic_code's optional fields.
         use_inherits = dimension.get('use_inherits', None)
         if use_inherits is None:
             use_inherits = not (
-                any(col in columns for col in ('name', 'code_parent_id'))
+                any(col in columns for col in ('name', 'nd_id'))
                 or nmspc.get('_inherits', False)
                 or nmspc.get('_inherit', False)
             )
 
+        use_code_name_methods = dimension.get('use_code_name_methods', False)
+
         if use_inherits:
             inherits = nmspc.get('_inherits', {})
             inherits['analytic.code'] = column
@@ -197,6 +212,36 @@
                 ondelete='restrict'
             )
 
+        rel_cols = [cols for cols in [
+                rel_description + ('description', 'char'),
+                rel_active + ('active', 'boolean')
+            ] if len(cols) == 4
+        ]
+
+        if rel_cols:
+            # NOT a method nor a class member. 'self' is the analytic_code OSV.
+            def _record_from_code_id(self, cr, uid, ids, context=None):
+                """Get the entries to update from the modified codes."""
+                osv = self.pool.get(orm_name)
+                domain = [(column, 'in', ids)]
+                return osv.search(cr, uid, domain, context=context)
+
+            for string, model_col, code_col, data_type in rel_cols:
+                columns[model_col] = fields.related(
+                    column,
+                    code_col,
+                    string=string,
+                    type=data_type,
+                    relation="analytic.code",
+                    store={
+                        'analytic.code': (
+                            _record_from_code_id,
+                            [code_col],
+                            10
+                        )
+                    }
+                )
+
         # In order to preserve inheritance, possible overrides, and OEMetaSL's
         # expected behavior, work on a new class that inherits the given bases,
         # then make our model class inherit from this class.
@@ -295,4 +340,37 @@
                     cr, uid, ids, vals, context=context
                 )
 
+        if use_code_name_methods:
+
+            @AddMethod(superclass)
+            def name_get(self, cr, uid, ids, context=None):
+                """Return the analytic code's name."""
+
+                code_osv = self.pool.get('analytic.code')
+                code_reads = self.read(cr, uid, ids, [column], context=context)
+                code_ids = {
+                    code_read[column][0]: code_read['id']
+                    for code_read in code_reads
+                    if code_read[column] is not False
+                }
+                names = code_osv.name_get(cr, uid, code_ids.keys(), context)
+                return [(code_ids[cid], name) for cid, name in names]
+
+            @AddMethod(superclass)
+            def name_search(
+                self, cr, uid, name, args=None, operator='ilike', context=None,
+                limit=100
+            ):
+                """Return the records whose analytic code matches the name."""
+
+                code_osv = self.pool.get('analytic.code')
+                args.append(('nd_id', '=', self._bound_dimension_id))
+                code_res = code_osv.name_search(
+                    cr, uid, name, args, operator, context, limit
+                )
+                code_ids, names = sorted(zip(*code_res), key=lambda t: t[0])
+                dom = [(column, 'in', code_ids)]
+                ids = self.search(cr, uid, dom, order=column, context=context)
+                return zip(ids, names)
+
         return (superclass,)
diff --git a/README b/README
--- a/README
+++ b/README
@@ -117,7 +117,10 @@
     'ref_id': 'school_analytic_dimension',
     'ref_module': 'my_module',
     'sync_parent': False,
+    'rel_description': True,
+    'rel_active': (u"Active", 'active_code'),
     'use_inherits': False,
+    'use_code_name_methods': False,
 }
 
 
@@ -126,7 +129,7 @@
 name (= _description or _name): The name of the analytic dimension.
 This name is only used when creating the dimension in the database.
 
-column (analytic_id): The column that links each record to an analytic code.
+column (analytic_id): The field that links each record to an analytic code.
 
 ref_id (= _name + analytic_dimension_id): The external ID that will
 be used by the analytic dimension. By setting this value, you can allow two
@@ -146,3 +149,20 @@
 The default value is True if the model has no 'name' and 'code_parent_id' field
 as well as no inheritance of any kind, and False otherwise. If the object has
 inheritances that do not cause conflicts, you can set it to True.
+
+rel_active (False): Create a related field in the model, targeting the
+analytic code field 'active' and with an appropriate store parameter.
+This is useful when the model doesn't inherit analytic_code and/or when it
+already has a field named 'active'.
+Can take a pair of string values: (field label, field name).
+If given a string, the default field name 'active' will be used.
+If given True, the default field label 'Active' will also be used.
+
+rel_description (False): Same as rel_active for the code field 'description'.
+If given a string, the default field name 'description' will be used.
+If given True, the default field label 'Description' will also be used.
+
+use_code_name_methods (False): Set to True in order to override the methods
+name_get and name_search, using those of analytic code.
+This allows the analytic code's description to be displayed (and searched)
+along with the entry's name in many2one fields targeting the model.
diff --git a/analytic_dimension.xml b/analytic_dimension.xml
--- a/analytic_dimension.xml
+++ b/analytic_dimension.xml
@@ -6,8 +6,8 @@
             <field name="model">analytic.code</field>
             <field name="arch" type="xml">
                 <search string="Analytic Codes"> 
-                    <!--<field name="name" filter_domain="['|',(name', 'ilike', self), ('nd_id', 'ilike', self)]"/>
-                    Easy searchable not working "error length of undefined"-->
+                    <field name="name" filter_domain="['|',('name', 'ilike', self), ('description', 'ilike', self)]"/>
+                    <field name="nd_name" filter_domain="[('nd_name', 'ilike', self)]"/>
                     <group expand="0" string="Group By...">
                         <filter string="Code Dimension" domain="" context="{'group_by':'nd_id'}"/>
                     </group>