[MERGE] forward port of branch 7.0 up to 4bfcbb2
authorChristophe Simonis <chs@odoo.com>
Wed, 25 Jun 2014 09:35:11 +0000 (11:35 +0200)
committerChristophe Simonis <chs@odoo.com>
Wed, 25 Jun 2014 09:35:11 +0000 (11:35 +0200)
1  2 
addons/crm_claim/crm_claim.py
openerp/osv/expression.py
openerp/osv/fields.py

  #
  ##############################################################################
  
 -from openerp.addons.base_status.base_stage import base_stage
 -import binascii
 +import openerp
  from openerp.addons.crm import crm
  from openerp.osv import fields, osv
 -import time
  from openerp import tools
  from openerp.tools.translate import _
  from openerp.tools import html2plaintext
  
 -CRM_CLAIM_PENDING_STATES = (
 -    crm.AVAILABLE_STATES[2][0], # Cancelled
 -    crm.AVAILABLE_STATES[3][0], # Done
 -    crm.AVAILABLE_STATES[4][0], # Pending
 -)
  
  class crm_claim_stage(osv.osv):
      """ Model for claim stages. This models the main stages of a claim
@@@ -43,6 -50,9 +43,6 @@@
          'sequence': fields.integer('Sequence', help="Used to order stages. Lower is better."),
          'section_ids':fields.many2many('crm.case.section', 'section_claim_stage_rel', 'stage_id', 'section_id', string='Sections',
                          help="Link between stages and sales teams. When set, this limitate the current stage to the selected sales teams."),
 -        'state': fields.selection(crm.AVAILABLE_STATES, 'Status', required=True, help="The related status for the stage. The status of your document will automatically change regarding the selected stage. For example, if a stage is related to the status 'Close', when your document reaches this stage, it will be automatically have the 'closed' status."),
 -        'case_refused': fields.boolean('Refused stage',
 -                        help='Refused stages are specific stages for done.'),
          'case_default': fields.boolean('Common to All Teams',
                          help="If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams."),
          'fold': fields.boolean('Hide in Views when Empty',
  
      _defaults = {
          'sequence': lambda *args: 1,
 -        'state': 'draft',
          'fold': False,
 -        'case_refused': False,
      }
  
 -class crm_claim(base_stage, osv.osv):
 +class crm_claim(osv.osv):
      """ Crm claim
      """
      _name = "crm.claim"
      _order = "priority,date desc"
      _inherit = ['mail.thread']
  
 +    def _get_default_section_id(self, cr, uid, context=None):
 +        """ Gives default section by checking if present in the context """
 +        return self.pool.get('crm.lead')._resolve_section_id_from_context(cr, uid, context=context) or False
 +
 +    def _get_default_stage_id(self, cr, uid, context=None):
 +        """ Gives default stage_id """
 +        section_id = self._get_default_section_id(cr, uid, context=context)
 +        return self.stage_find(cr, uid, [], section_id, [('sequence', '=', '1')], context=context)
 +
      _columns = {
          'id': fields.integer('ID', readonly=True),
          'name': fields.char('Claim Subject', size=128, required=True),
@@@ -84,7 -87,7 +84,7 @@@
          'date_deadline': fields.date('Deadline'),
          'date_closed': fields.datetime('Closed', readonly=True),
          'date': fields.datetime('Claim Date', select=True),
 -        'ref' : fields.reference('Reference', selection=crm._links_get, size=128),
 +        'ref': fields.reference('Reference', selection=openerp.addons.base.res.res_request.referencable_models),
          'categ_id': fields.many2one('crm.case.categ', 'Category', \
                              domain="[('section_id','=',section_id),\
                              ('object_id.model', '=', 'crm.claim')]"),
          'email_from': fields.char('Email', size=128, help="Destination email for email gateway."),
          'partner_phone': fields.char('Phone', size=32),
          'stage_id': fields.many2one ('crm.claim.stage', 'Stage', track_visibility='onchange',
 -                domain="['&',('fold', '=', False),'|', ('section_ids', '=', section_id), ('case_default', '=', True)]"),
 +                domain="['|', ('section_ids', '=', section_id), ('case_default', '=', True)]"),
          'cause': fields.text('Root Cause'),
      }
  
      _defaults = {
 -        'user_id':  lambda s, cr, uid, c: s._get_default_user(cr, uid, c),
 -        'partner_id':  lambda s, cr, uid, c: s._get_default_partner(cr, uid, c),
 -        'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
 +        'user_id': lambda s, cr, uid, c: uid,
          'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
          'date': fields.datetime.now,
          'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
          'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
          'active': lambda *a: 1,
 -        'stage_id':lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c)
 +        'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c)
      }
  
      def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
              return stage_ids[0]
          return False
  
 -    def case_refuse(self, cr, uid, ids, context=None):
 -        """ Mark the case as refused: state=done and case_refused=True """
 -        for lead in self.browse(cr, uid, ids):
 -            stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, ['&', ('state', '=', 'done'), ('case_refused', '=', True)], context=context)
 -            if stage_id:
 -                self.case_set(cr, uid, [lead.id], values_to_update={}, new_stage_id=stage_id, context=context)
 -        return True
 -
 -    def onchange_partner_id(self, cr, uid, ids, part, email=False):
 +    def onchange_partner_id(self, cr, uid, ids, partner_id, email=False, context=None):
          """This function returns value of partner address based on partner
 -           :param part: Partner's id
             :param email: ignored
          """
 -        if not part:
 -            return {'value': {'email_from': False,
 -                              'partner_phone': False
 -                            }
 -                   }
 -        address = self.pool.get('res.partner').browse(cr, uid, part)
 +        if not partner_id:
 +            return {'value': {'email_from': False, 'partner_phone': False}}
 +        address = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
          return {'value': {'email_from': address.email, 'partner_phone': address.phone}}
  
 +    def create(self, cr, uid, vals, context=None):
 +        if context is None:
 +            context = {}
 +        if vals.get('section_id') and not context.get('default_section_id'):
 +            context['default_section_id'] = vals.get('section_id')
 +
 +        # context: no_log, because subtype already handle this
 +        return super(crm_claim, self).create(cr, uid, vals, context=context)
 +
+     def copy(self, cr, uid, id, default=None, context=None):
+         claim = self.browse(cr, uid, id, context=context)
+         default = dict(default or {},
+             stage_id = self._get_default_stage_id(cr, uid, context=context),
+             name = _('%s (copy)') % claim.name)
+         return super(crm_claim, self).copy(cr, uid, id, default, context=context)
      # -------------------------------------------------------
      # Mail gateway
      # -------------------------------------------------------
@@@ -140,6 -140,7 +140,6 @@@ from openerp.osv import field
  from openerp.osv.orm import MAGIC_COLUMNS
  import openerp.tools as tools
  
 -#.apidoc title: Domain Expressions
  
  # Domain operators.
  NOT_OPERATOR = '!'
@@@ -762,7 -763,7 +762,7 @@@ class expression(object)
              field_path = left.split('.', 1)
              field = working_model._columns.get(field_path[0])
              if field and field._obj:
 -                relational_model = working_model.pool.get(field._obj)
 +                relational_model = working_model.pool[field._obj]
              else:
                  relational_model = None
  
                  # comments about inherits'd fields
                  #  { 'field_name': ('parent_model', 'm2o_field_to_reach_parent',
                  #                    field_column_obj, origina_parent_model), ... }
 -                next_model = working_model.pool.get(working_model._inherit_fields[field_path[0]][0])
 +                next_model = working_model.pool[working_model._inherit_fields[field_path[0]][0]]
                  leaf.add_join_context(next_model, working_model._inherits[next_model._name], 'id', working_model._inherits[next_model._name])
                  push(leaf)
  
  
              else:
                  if field._type == 'datetime' and right and len(right) == 10:
 -                    if operator in ('>', '>='):
 -                        right += ' 00:00:00'
 -                    elif operator in ('<', '<='):
 +                    if operator in ('>', '<='):
                          right += ' 23:59:59'
 +                    else:
 +                        right += ' 00:00:00'
                      push(create_substitution_leaf(leaf, (left, operator, right), working_model))
  
                  elif field.translate and right:
  
                      unaccent = self._unaccent if sql_operator.endswith('like') else lambda x: x
  
-                     trans_left = unaccent('value')
-                     quote_left = unaccent(_quote(left))
                      instr = unaccent('%s')
  
                      if sql_operator == 'in':
                          # params will be flatten by to_sql() => expand the placeholders
                          instr = '(%s)' % ', '.join(['%s'] * len(right))
  
-                     subselect = """(SELECT res_id
-                                       FROM ir_translation
-                                      WHERE name = %s
-                                        AND lang = %s
-                                        AND type = %s
-                                        AND {trans_left} {operator} {right}
-                                    ) UNION (
-                                     SELECT id
-                                       FROM "{table}"
-                                      WHERE {left} {operator} {right}
-                                    )
-                                 """.format(trans_left=trans_left, operator=sql_operator,
-                                            right=instr, table=working_model._table, left=quote_left)
+                     subselect = """WITH temp_irt_current (id, name) as (
+                             SELECT ct.id, coalesce(it.value,ct.{quote_left})
+                             FROM {current_table} ct 
+                             LEFT JOIN ir_translation it ON (it.name = %s and 
+                                         it.lang = %s and 
+                                         it.type = %s and 
+                                         it.res_id = ct.id and 
+                                         it.value != '')
+                             ) 
+                             SELECT id FROM temp_irt_current WHERE {name} {operator} {right} order by name
+                             """.format(current_table=working_model._table, quote_left=_quote(left), name=unaccent('name'), 
+                                        operator=sql_operator, right=instr)
  
                      params = (
                          working_model._name + ',' + left,
                          context.get('lang') or 'en_US',
                          'model',
                          right,
-                         right,
                      )
                      push(create_substitution_leaf(leaf, ('id', inselect_operator, (subselect, params)), working_model))
  
                      if left == 'id':
                          instr = ','.join(['%s'] * len(params))
                      else:
 -                        instr = ','.join([model._columns[left]._symbol_set[0]] * len(params))
 +                        ss = model._columns[left]._symbol_set
 +                        instr = ','.join([ss[0]] * len(params))
 +                        params = map(ss[1], params)
                      query = '(%s."%s" %s (%s))' % (table_alias, left, operator, instr)
                  else:
                      # The case for (left, 'in', []) or (left, 'not in', []).
diff --combined openerp/osv/fields.py
@@@ -37,7 -37,6 +37,7 @@@
  
  import base64
  import datetime as DT
 +import functools
  import logging
  import pytz
  import re
@@@ -110,6 -109,7 +110,6 @@@ class _column(object)
          self._context = context
          self.write = False
          self.read = False
 -        self.view_load = 0
          self.select = select
          self.manual = manual
          self.selectable = True
@@@ -182,7 -182,7 +182,7 @@@ class reference(_column)
      _type = 'reference'
      _classic_read = False # post-process to handle missing target
  
 -    def __init__(self, string, selection, size, **args):
 +    def __init__(self, string, selection, size=None, **args):
          _column.__init__(self, string=string, size=size, selection=selection, **args)
  
      def get(self, cr, obj, ids, name, uid=None, context=None, values=None):
              result[value['id']] = value[name]
              if value[name]:
                  model, res_id = value[name].split(',')
 -                if not obj.pool.get(model).exists(cr, uid, [int(res_id)], context=context):
 +                if not obj.pool[model].exists(cr, uid, [int(res_id)], context=context):
                      result[value['id']] = False
          return result
  
              # reference fields have a 'model,id'-like value, that we need to convert
              # to a real name
              model_name, res_id = value.split(',')
 -            model = obj.pool.get(model_name)
 -            if model and res_id:
 +            if model_name in obj.pool and res_id:
 +                model = obj.pool[model_name]
                  names = model.name_get(cr, uid, [int(res_id)], context=context)
                  return names[0][1] if names else False
          return tools.ustr(value)
@@@ -236,24 -236,15 +236,24 @@@ class char(_column)
  class text(_column):
      _type = 'text'
  
 +
  class html(text):
      _type = 'html'
      _symbol_c = '%s'
 -    def _symbol_f(x):
 -        if x is None or x == False:
 +
 +    def _symbol_set_html(self, value):
 +        if value is None or value is False:
              return None
 -        return html_sanitize(x)
 -        
 -    _symbol_set = (_symbol_c, _symbol_f)
 +        if not self._sanitize:
 +            return value
 +        return html_sanitize(value)
 +
 +    def __init__(self, string='unknown', sanitize=True, **args):
 +        super(html, self).__init__(string=string, **args)
 +        self._sanitize = sanitize
 +        # symbol_set redefinition because of sanitize specific behavior
 +        self._symbol_f = self._symbol_set_html
 +        self._symbol_set = (self._symbol_c, self._symbol_f)
  
  import __builtin__
  
@@@ -282,21 -273,6 +282,21 @@@ class float(_column)
  class date(_column):
      _type = 'date'
  
 +    MONTHS = [
 +        ('01', 'January'),
 +        ('02', 'February'),
 +        ('03', 'March'),
 +        ('04', 'April'),
 +        ('05', 'May'),
 +        ('06', 'June'),
 +        ('07', 'July'),
 +        ('08', 'August'),
 +        ('09', 'September'),
 +        ('10', 'October'),
 +        ('11', 'November'),
 +        ('12', 'December')
 +    ]
 +
      @staticmethod
      def today(*args):
          """ Returns the current date in a format fit for being a
  
  class datetime(_column):
      _type = 'datetime'
 +
 +    MONTHS = [
 +        ('01', 'January'),
 +        ('02', 'February'),
 +        ('03', 'March'),
 +        ('04', 'April'),
 +        ('05', 'May'),
 +        ('06', 'June'),
 +        ('07', 'July'),
 +        ('08', 'August'),
 +        ('09', 'September'),
 +        ('10', 'October'),
 +        ('11', 'November'),
 +        ('12', 'December')
 +    ]
 +
      @staticmethod
      def now(*args):
          """ Returns the current datetime in a format fit for being a
@@@ -461,39 -421,6 +461,39 @@@ class selection(_column)
          _column.__init__(self, string=string, **args)
          self.selection = selection
  
 +    @classmethod
 +    def reify(cls, cr, uid, model, field, context=None):
 +        """ Munges the field's ``selection`` attribute as necessary to get
 +        something useable out of it: calls it if it's a function, applies
 +        translations to labels if it's not.
 +
 +        A callable ``selection`` is considered translated on its own.
 +
 +        :param orm.Model model:
 +        :param _column field:
 +        """
 +        if callable(field.selection):
 +            return field.selection(model, cr, uid, context)
 +
 +        if not (context and 'lang' in context):
 +            return field.selection
 +
 +        # field_to_dict isn't given a field name, only a field object, we
 +        # need to get the name back in order to perform the translation lookup
 +        field_name = next(
 +            name for name, column in model._columns.iteritems()
 +            if column == field)
 +
 +        translation_filter = "%s,%s" % (model._name, field_name)
 +        translate = functools.partial(
 +            model.pool['ir.translation']._get_source,
 +            cr, uid, translation_filter, 'selection', context['lang'])
 +
 +        return [
 +            (value, translate(label))
 +            for value, label in field.selection
 +        ]
 +
  # ---------------------------------------------------------
  # Relationals fields
  # ---------------------------------------------------------
@@@ -531,7 -458,7 +531,7 @@@ class many2one(_column)
              res[r['id']] = r[name]
          for id in ids:
              res.setdefault(id, '')
 -        obj = obj.pool.get(self._obj)
 +        obj = obj.pool[self._obj]
  
          # build a dictionary of the form {'id_of_distant_resource': name_of_distant_resource}
          # we use uid=1 because the visibility of a many2one field value (just id and name)
      def set(self, cr, obj_src, id, field, values, user=None, context=None):
          if not context:
              context = {}
 -        obj = obj_src.pool.get(self._obj)
 -        self._table = obj_src.pool.get(self._obj)._table
 +        obj = obj_src.pool[self._obj]
 +        self._table = obj._table
          if type(values) == type([]):
              for act in values:
                  if act[0] == 0:
                  cr.execute('update '+obj_src._table+' set '+field+'=null where id=%s', (id,))
  
      def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None):
 -        return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', 'like', value)], offset, limit, context=context)
 +        return obj.pool[self._obj].search(cr, uid, args+self._domain+[('name', 'like', value)], offset, limit, context=context)
  
      
      @classmethod
@@@ -608,14 -535,10 +608,14 @@@ class one2many(_column)
              res[id] = []
  
          domain = self._domain(obj) if callable(self._domain) else self._domain
 -        ids2 = obj.pool.get(self._obj).search(cr, user, domain + [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
 -        for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
 -            if r[self._fields_id] in res:
 -                res[r[self._fields_id]].append(r['id'])
 +        model = obj.pool[self._obj]
 +        ids2 = model.search(cr, user, domain + [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
 +        if len(ids) != 1:
 +            for r in model._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
 +                if r[self._fields_id] in res:
 +                    res[r[self._fields_id]].append(r['id'])
 +        else:
 +            res[ids[0]] = ids2
          return res
  
      def set(self, cr, obj, id, field, values, user=None, context=None):
          context['no_store_function'] = True
          if not values:
              return
 -        _table = obj.pool.get(self._obj)._table
 -        obj = obj.pool.get(self._obj)
 +        obj = obj.pool[self._obj]
 +        _table = obj._table
          for act in values:
              if act[0] == 0:
                  act[2][self._fields_id] = id
  
      def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None):
          domain = self._domain(obj) if callable(self._domain) else self._domain
 -        return obj.pool.get(self._obj).name_search(cr, uid, value, domain, operator, context=context,limit=limit)
 +        return obj.pool[self._obj].name_search(cr, uid, value, domain, operator, context=context,limit=limit)
  
      
      @classmethod
@@@ -750,7 -673,7 +750,7 @@@ class many2many(_column)
          tbl, col1, col2 = self._rel, self._id1, self._id2
          if not all((tbl, col1, col2)):
              # the default table name is based on the stable alphabetical order of tables
 -            dest_model = source_model.pool.get(self._obj)
 +            dest_model = source_model.pool[self._obj]
              tables = tuple(sorted([source_model._table, dest_model._table]))
              if not tbl:
                  assert tables[0] != tables[1], 'Implicit/Canonical naming of m2m relationship table '\
              _logger.warning(
                  "Specifying offset at a many2many.get() is deprecated and may"
                  " produce unpredictable results.")
 -        obj = model.pool.get(self._obj)
 +        obj = model.pool[self._obj]
          rel, id1, id2 = self._sql_names(model)
  
          # static domains are lists, and are evaluated both here and on client-side, while string
          if not values:
              return
          rel, id1, id2 = self._sql_names(model)
 -        obj = model.pool.get(self._obj)
 +        obj = model.pool[self._obj]
          for act in values:
              if not (isinstance(act, list) or isinstance(act, tuple)) or not act:
                  continue
      # TODO: use a name_search
      #
      def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None):
 -        return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', operator, value)], offset, limit, context=context)
 +        return obj.pool[self._obj].search(cr, uid, args+self._domain+[('name', operator, value)], offset, limit, context=context)
  
      @classmethod
      def _as_display_name(cls, field, cr, uid, obj, value, context=None):
@@@ -1198,7 -1121,7 +1198,7 @@@ class function(_column)
          if field_type == "many2one":
              # make the result a tuple if it is not already one
              if isinstance(value, (int,long)) and hasattr(obj._columns[field], 'relation'):
 -                obj_model = obj.pool.get(obj._columns[field].relation)
 +                obj_model = obj.pool[obj._columns[field].relation]
                  dict_names = dict(obj_model.name_get(cr, SUPERUSER_ID, [value], context))
                  result = (value, dict_names[value])
  
@@@ -1303,7 -1226,7 +1303,7 @@@ class related(function)
              # Perform name_get as root, as seeing the name of a related object depends on
              # access right of source document, not target, so user may not have access.
              value_ids = list(set(value.id for value in res.itervalues() if value))
 -            value_name = dict(obj.pool.get(self._obj).name_get(cr, SUPERUSER_ID, value_ids, context=context))
 +            value_name = dict(obj.pool[self._obj].name_get(cr, SUPERUSER_ID, value_ids, context=context))
              res = dict((id, value and (value.id, value_name[value.id])) for id, value in res.iteritems())
  
          elif self._type in ('one2many', 'many2many'):
@@@ -1357,7 -1280,7 +1357,7 @@@ class sparse(function)
          elif self._type == 'one2many':
              if not read_value:
                  read_value = []
 -            relation_obj = obj.pool.get(self.relation)
 +            relation_obj = obj.pool[self.relation]
              for vals in value:
                  assert vals[0] in (0,1,2), 'Unsupported o2m value for sparse field: %s' % vals
                  if vals[0] == 0:
                      value = value or []
                      if value:
                          # filter out deleted records as superuser
 -                        relation_obj = obj.pool.get(obj._columns[field_name].relation)
 +                        relation_obj = obj.pool[obj._columns[field_name].relation]
                          value = relation_obj.exists(cr, openerp.SUPERUSER_ID, value)
                  if type(value) in (int,long) and field_type == 'many2one':
 -                    relation_obj = obj.pool.get(obj._columns[field_name].relation)
 +                    relation_obj = obj.pool[obj._columns[field_name].relation]
                      # check for deleted record as superuser
                      if not relation_obj.exists(cr, openerp.SUPERUSER_ID, [value]):
                          value = False
@@@ -1433,7 -1356,7 +1433,7 @@@ class dummy(function)
      def __init__(self, *arg, **args):
          self.arg = arg
          self._relations = []
 -        super(dummy, self).__init__(self._fnct_read, arg, self._fnct_write, fnct_inv_arg=arg, fnct_search=None, **args)
 +        super(dummy, self).__init__(self._fnct_read, arg, self._fnct_write, fnct_inv_arg=arg, fnct_search=self._fnct_search, **args)
  
  # ---------------------------------------------------------
  # Serialized fields
@@@ -1505,7 -1428,8 +1505,8 @@@ class property(function)
          default_val = self._get_default(obj, cr, uid, prop_name, context)
  
          property_create = False
-         if isinstance(default_val, openerp.osv.orm.browse_record):
+         if isinstance(default_val, (openerp.osv.orm.browse_record,
+                                     openerp.osv.orm.browse_null)):
              if default_val.id != id_val:
                  property_create = True
          elif default_val != id_val:
                  # not target, so user may not have access) in order to avoid
                  # pointing on an unexisting record.
                  if property_destination_obj:
 -                    if res[id][prop_name] and obj.pool.get(property_destination_obj).exists(cr, SUPERUSER_ID, res[id][prop_name].id):
 +                    if res[id][prop_name] and obj.pool[property_destination_obj].exists(cr, SUPERUSER_ID, res[id][prop_name].id):
                          name_get_ids[id] = res[id][prop_name].id
                      else:
                          res[id][prop_name] = False
                  # name_get as root (as seeing the name of a related
                  # object depends on access right of source document,
                  # not target, so user may not have access.)
 -                name_get_values = dict(obj.pool.get(property_destination_obj).name_get(cr, SUPERUSER_ID, name_get_ids.values(), context=context))
 +                name_get_values = dict(obj.pool[property_destination_obj].name_get(cr, SUPERUSER_ID, name_get_ids.values(), context=context))
                  # the property field is a m2o, we need to return a tuple with (id, name)
                  for k, v in name_get_ids.iteritems():
                      if res[k][prop_name]:
              self.field_id[cr.dbname] = res and res[0]
          return self.field_id[cr.dbname]
  
 -    def __init__(self, obj_prop, **args):
 -        # TODO remove obj_prop parameter (use many2one type)
 +
 +    def __init__(self, **args):
          self.field_id = {}
 -        function.__init__(self, self._fnct_read, False, self._fnct_write,
 -                          obj_prop, multi='properties', **args)
 +        if 'view_load' in args:
 +            _logger.warning("view_load attribute is deprecated on ir.fields. Args: %r", args)
 +        obj = 'relation' in args and args['relation'] or ''
 +        function.__init__(self, self._fnct_read, False, self._fnct_write, obj=obj, multi='properties', **args)
  
      def restart(self):
          self.field_id = {}
@@@ -1618,7 -1540,11 +1619,7 @@@ def field_to_dict(model, cr, user, fiel
              res[arg] = getattr(field, arg)
  
      if hasattr(field, 'selection'):
 -        if isinstance(field.selection, (tuple, list)):
 -            res['selection'] = field.selection
 -        else:
 -            # call the 'dynamic selection' function
 -            res['selection'] = field.selection(model, cr, user, context)
 +        res['selection'] = selection.reify(cr, user, model, field, context=context)
      if res['type'] in ('one2many', 'many2many', 'many2one'):
          res['relation'] = field._obj
          res['domain'] = field._domain(model) if callable(field._domain) else field._domain