bugfix
[odoo/odoo.git] / bin / osv / orm.py
index 9975cf1..92fb7f9 100644 (file)
@@ -41,6 +41,7 @@
 import calendar
 import copy
 import datetime
+import logging
 import pickle
 import random
 import re
@@ -49,15 +50,33 @@ import sys
 import time
 import traceback
 import types
-from lxml import etree
 
 import fields
 import netsvc
 import tools
-from tools.config import config
 from tools.translate import _
 
-regex_order = re.compile('^([a-z0-9_]+( *desc| *asc)?( *, *|))+$', re.I)
+import copy
+import sys
+
+try:
+    from lxml import etree
+except ImportError:
+    sys.stderr.write("ERROR: Import lxml module\n")
+    sys.stderr.write("ERROR: Try to install the python-lxml package\n")
+
+from tools.config import config
+
+regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
+
+
+POSTGRES_CONFDELTYPES = {
+    'RESTRICT': 'r',
+    'NO ACTION': 'a',
+    'CASCADE': 'c',
+    'SET NULL': 'n',
+    'SET DEFAULT': 'd',
+}
 
 def last_day_of_current_month():
     today = datetime.date.today()
@@ -130,6 +149,8 @@ class browse_record(object):
         self._id = id
         self._table = table
         self._table_name = self._table._name
+        self.__logger = logging.getLogger(
+            'osv.browse_record.' + self._table_name)
         self._context = context
         self._fields_process = fields_process
 
@@ -201,14 +222,14 @@ class browse_record(object):
 
             if not datas:
                 # Where did those ids come from? Perhaps old entries in ir_model_dat?
-                self.logger.notifyChannel("browse_record", netsvc.LOG_WARNING,
-                    "No datas found for ids %s in %s \n%s" % (
-                        ids, self, ''.join(traceback.format_exc())))
+                self.__logger.warn("No datas found for ids %s in %s",
+                                   ids, self)
                 raise KeyError('Field %s not found in %s'%(name,self))
             # create browse records for 'remote' objects
             for data in datas:
                 if len(str(data['id']).split('-')) > 1:
                     data['id'] = int(str(data['id']).split('-')[0])
+                new_data = {}
                 for n, f in ffields:
                     if f._type in ('many2one', 'one2one'):
                         if data[n]:
@@ -219,14 +240,37 @@ class browse_record(object):
                             else:
                                 ids2 = data[n]
                             if ids2:
-                                data[n] = browse_record(self._cr, self._uid, ids2, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process)
+                                # FIXME: this happen when a _inherits object
+                                #        overwrite a field of it parent. Need
+                                #        testing to be sure we got the right
+                                #        object and not the parent one.
+                                if not isinstance(ids2, browse_record):
+                                    new_data[n] = browse_record(self._cr,
+                                        self._uid, ids2, obj, self._cache,
+                                        context=self._context,
+                                        list_class=self._list_class,
+                                        fields_process=self._fields_process)
                             else:
-                                data[n] = browse_null()
+                                new_data[n] = browse_null()
                         else:
-                            data[n] = browse_null()
+                            new_data[n] = browse_null()
                     elif f._type in ('one2many', 'many2many') and len(data[n]):
-                        data[n] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool.get(f._obj), self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in data[n]], self._context)
-                self._data[data['id']].update(data)
+                        new_data[n] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool.get(f._obj), self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in data[n]], self._context)
+                    elif f._type in ('reference'):
+                        if data[n]:
+                            if isinstance(data[n], browse_record):
+                                new_data[n] = data[n]
+                            else:
+                                ref_obj, ref_id = data[n].split(',')
+                                ref_id = long(ref_id)
+                                obj = self._table.pool.get(ref_obj)
+                                compids = False
+                                new_data[n] = browse_record(self._cr, self._uid, ref_id, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process)
+                        else:
+                            new_data[n] = browse_null()
+                    else:
+                        new_data[n] = data[n]
+                self._data[data['id']].update(new_data)
         if not name in self._data[self._id]:
             #how did this happen?
             self.logger.notifyChannel("browse_record", netsvc.LOG_ERROR,
@@ -292,7 +336,7 @@ def get_pg_type(f):
         f_type = (type_dict[type(f)], type_dict[type(f)])
     elif isinstance(f, fields.float):
         if f.digits:
-            f_type = ('numeric', 'NUMERIC(%d,%d)' % (f.digits[0], f.digits[1]))
+            f_type = ('numeric', 'NUMERIC')
         else:
             f_type = ('float8', 'DOUBLE PRECISION')
     elif isinstance(f, (fields.char, fields.reference)):
@@ -314,7 +358,7 @@ def get_pg_type(f):
         f_type = (type_dict[t], type_dict[t])
     elif isinstance(f, fields.function) and f._type == 'float':
         if f.digits:
-            f_type = ('numeric', 'NUMERIC(%d,%d)' % (f.digits[0], f.digits[1]))
+            f_type = ('numeric', 'NUMERIC')
         else:
             f_type = ('float8', 'DOUBLE PRECISION')
     elif isinstance(f, fields.function) and f._type == 'selection':
@@ -347,6 +391,10 @@ class orm_template(object):
 
     CONCURRENCY_CHECK_FIELD = '__last_update'
 
+    def view_init(self, cr , uid , fields_list, context=None):
+        """Override this method to do specific things when a view on the object is opened."""
+        pass
+
     def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None):
         raise _('The read_group method is not implemented on this object !')
 
@@ -360,7 +408,7 @@ class orm_template(object):
             model_id = cr.fetchone()[0]
         if 'module' in context:
             name_id = 'model_'+self._name.replace('.','_')
-            cr.execute('select * from ir_model_data where name=%s and res_id=%s', (name_id,model_id))
+            cr.execute('select * from ir_model_data where name=%s and res_id=%s and module=%s', (name_id,model_id,context['module']))
             if not cr.rowcount:
                 cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \
                     (name_id, context['module'], 'ir.model', model_id)
@@ -386,6 +434,7 @@ class orm_template(object):
                 'readonly':(f.readonly and 1) or 0,
                 'required':(f.required and 1) or 0,
                 'selectable' : (f.selectable and 1) or 0,
+                'relation_field': (f._type=='one2many' and isinstance(f,fields.one2many)) and f._fields_id or '',
             }
             # When its a custom field,it does not contain f.select
             if context.get('field_state','base') == 'manual':
@@ -401,13 +450,13 @@ class orm_template(object):
                 vals['id'] = id
                 cr.execute("""INSERT INTO ir_model_fields (
                     id, model_id, model, name, field_description, ttype,
-                    relation,view_load,state,select_level
+                    relation,view_load,state,select_level,relation_field
                 ) VALUES (
-                    %s,%s,%s,%s,%s,%s,%s,%s,%s,%s
+                    %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s
                 )""", (
                     id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'],
                      vals['relation'], bool(vals['view_load']), 'base',
-                    vals['select_level']
+                    vals['select_level'],vals['relation_field']
                 ))
                 if 'module' in context:
                     name1 = 'field_' + self._table + '_' + k
@@ -424,12 +473,12 @@ class orm_template(object):
                         cr.commit()
                         cr.execute("""UPDATE ir_model_fields SET
                             model_id=%s, field_description=%s, ttype=%s, relation=%s,
-                            view_load=%s, select_level=%s, readonly=%s ,required=%s, selectable=%s
+                            view_load=%s, select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s
                         WHERE
                             model=%s AND name=%s""", (
                                 vals['model_id'], vals['field_description'], vals['ttype'],
                                 vals['relation'], bool(vals['view_load']),
-                                vals['select_level'], bool(vals['readonly']),bool(vals['required']),bool(vals['selectable']),vals['model'], vals['name']
+                                vals['select_level'], bool(vals['readonly']),bool(vals['required']),bool(vals['selectable']),vals['relation_field'],vals['model'], vals['name']
                             ))
                         continue
         cr.commit()
@@ -687,6 +736,7 @@ class orm_template(object):
                             else:
                                 module, xml_id = current_module, line[i]
                             id = ir_model_data_obj._get_id(cr, uid, module, xml_id)
+
                             res_res_id = ir_model_data_obj.read(cr, uid, [id],
                                                 ['res_id'])
                             if res_res_id:
@@ -883,6 +933,8 @@ class orm_template(object):
                 if isinstance(e, osv.orm.except_orm ):
                     msg = _('Insertion Failed! ' + e[1])
                     return (-1, res, 'Line ' + str(counter) +' : ' + msg, '' )
+                #Raising Uncaught exception
+                raise
             for lang in translate:
                 context2 = context.copy()
                 context2['lang'] = lang
@@ -932,6 +984,17 @@ class orm_template(object):
             self._invalids.clear()
 
     def default_get(self, cr, uid, fields_list, context=None):
+        """ Set default values for the object's fields.
+
+        Returns a dict of {field_name:default_value}
+
+        Arguments:
+        `fields_list`: the fields for which the object doesn't have
+                       any value yet, and default values need to be
+                       provided. If fields outside this list are
+                       returned, the user-provided values will be
+                       overwritten.
+        """
         return {}
 
     def perm_read(self, cr, user, ids, context=None, details=True):
@@ -970,8 +1033,8 @@ class orm_template(object):
                 if fields and f not in fields:
                     continue
                 res[f] = {'type': self._columns[f]._type}
-                for arg in ('string', 'readonly', 'states', 'size', 'required',
-                        'change_default', 'translate', 'help', 'select', 'selectable'):
+                for arg in ('string', 'readonly', 'states', 'size', 'required', 'group_operator',
+                        'change_default', 'translate', 'help', 'select', 'selectable','parent_field'):
                     if getattr(self._columns[f], arg):
                         res[f][arg] = getattr(self._columns[f], arg)
                 if not read_access:
@@ -1065,6 +1128,7 @@ class orm_template(object):
                         if column._domain and not isinstance(column._domain, (str, unicode)):
                             dom = column._domain
                         dom += eval(node.get('domain','[]'), {'uid':user, 'time':time})
+                        context.update(eval(node.get('context','{}')))
                         attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', dom, context=context)
                         if (node.get('required') and not int(node.get('required'))) or not column.required:
                             attrs['selection'].append((False,''))
@@ -1150,7 +1214,21 @@ class orm_template(object):
             button.set('readonly', str(int(not can_click)))
 
         arch = etree.tostring(node, encoding="utf-8").replace('\t', '')
-        fields = self.fields_get(cr, user, fields_def.keys(), context)
+
+        #code for diagram view.
+        fields={}
+        if node.tag=='diagram':
+            if node.getchildren()[0].tag=='node':
+               node_fields=self.pool.get(node.getchildren()[0].get('object')).fields_get(cr, user, fields_def.keys(), context)
+            if node.getchildren()[1].tag=='arrow':
+               arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, fields_def.keys(), context)
+            for key,value in node_fields.items():
+                fields[key]=value
+            for key,value in arrow_fields.items():
+                fields[key]=value
+        else:
+            fields = self.fields_get(cr, user, fields_def.keys(), context)
+
         for field in fields_def:
             if field == 'id':
                 # sometime, the view may containt the (invisible) field 'id' needed for a domain (when 2 objects have cross references)
@@ -1166,7 +1244,6 @@ class orm_template(object):
                 msg += "\n\nEither you wrongly customised this view, or some modules bringing those views are not compatible with your current data model"
                 netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, msg)
                 raise except_orm('View error', msg)
-
         return arch, fields
 
     def __get_default_calendar_view(self):
@@ -1425,7 +1502,7 @@ class orm_template(object):
             if context and context.get('active_id',False):
                 data_menu = self.pool.get('ir.ui.menu').browse(cr, user, context['active_id'], context).action
                 if data_menu:
-                    act_id = int(data_menu.split(',')[1])
+                    act_id = data_menu.id
                     if act_id:
                         data_action = self.pool.get('ir.actions.act_window').browse(cr, user, [act_id], context)[0]
                         result['submenu'] = getattr(data_action,'menus', False)
@@ -1463,6 +1540,10 @@ class orm_template(object):
                 'action': resaction,
                 'relate': resrelate
             }
+        if result['type']=='form' and result['arch'].count("default_focus")>1:
+                msg = "Form View contain more than one default_focus attribute"
+                netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, msg)
+                raise except_orm('View Error !',msg)
         return result
 
     _view_look_dom_arch = __view_look_dom_arch
@@ -1641,6 +1722,7 @@ class orm_memory(orm_template):
         return id_new
 
     def default_get(self, cr, uid, fields_list, context=None):
+        self.view_init(cr, uid, fields_list, context)
         if not context:
             context = {}
         value = {}
@@ -1796,58 +1878,97 @@ class orm(orm_template):
     _protected = ['read','write','create','default_get','perm_read','unlink','fields_get','fields_view_get','search','name_get','distinct_field_get','name_search','copy','import_data','search_count', 'exists']
 
     def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None):
+        groupby_list = groupby
+        if isinstance(groupby, list):
+            groupby = groupby[0]
         context = context or {}
         self.pool.get('ir.model.access').check(cr, uid, self._name, 'read', context=context)
         if not fields:
             fields = self._columns.keys()
 
-        (qu1, qu2, tables) = self._where_calc(cr, uid, domain, context=context)
-        dom = self.pool.get('ir.rule').domain_get(cr, uid, self._name, context=context)
-        qu1 = qu1 + dom[0]
-        qu2 = qu2 + dom[1]
+        (where_clause, where_params, tables) = self._where_calc(cr, uid, domain, context=context)
+        dom = self.pool.get('ir.rule').domain_get(cr, uid, self._name, 'read', context=context)
+        where_clause = where_clause + dom[0]
+        where_params = where_params + dom[1]
         for t in dom[2]:
             if t not in tables:
                 tables.append(t)
-        if len(qu1):
-            qu1 = ' where '+string.join(qu1, ' and ')
+
+        # Take care of adding join(s) if groupby is an '_inherits'ed field
+        tables, where_clause = self._inherits_join_calc(groupby,tables,where_clause)
+
+        if len(where_clause):
+            where_clause = ' where '+string.join(where_clause, ' and ')
         else:
-            qu1 = ''
+            where_clause = ''
         limit_str = limit and ' limit %d' % limit or ''
         offset_str = offset and ' offset %d' % offset or ''
 
         fget = self.fields_get(cr, uid, fields)
         float_int_fields = filter(lambda x: fget[x]['type'] in ('float','integer'), fields)
         sum = {}
-        flist = groupby
+
+        group_by = groupby
+        if fget.get(groupby,False) and fget[groupby]['type'] in ('date','datetime'):
+            flist = "to_char(%s,'yyyy-mm') as %s "%(groupby,groupby)
+            groupby = "to_char(%s,'yyyy-mm')"%(groupby)
+        else:
+            flist = groupby
+
         fields_pre = [f for f in float_int_fields if
                    f == self.CONCURRENCY_CHECK_FIELD
                 or (f in self._columns and getattr(self._columns[f], '_classic_write'))]
         for f in fields_pre:
             if f not in ['id','sequence']:
-                flist += ',sum('+f+') as '+f
-        cr.execute('select min(id) as id,'+flist+' from ' + self._table +qu1+' group by '+ groupby + limit_str + offset_str,qu2)
+                operator = fget[f].get('group_operator','sum')
+                flist += ','+operator+'('+f+') as '+f
+
+        cr.execute('select min(%s.id) as id,' % self._table + flist + ' from ' + ','.join(tables) + where_clause + ' group by '+ groupby + limit_str + offset_str, where_params)
         alldata = {}
+        groupby = group_by
         for r in cr.dictfetchall():
+            for fld,val in r.items():
+                if val == None:r[fld] = False
             alldata[r['id']] = r
             del r['id']
-
         data = self.read(cr, uid, alldata.keys(), [groupby], context=context)
+        today = datetime.date.today()
+
         for d in data:
-            if fget.has_key(groupby):
-                if fget[groupby]['type'] == 'many2one':
-                    d[groupby] = d[groupby] and d[groupby][1] or ''
-                if fget[groupby]['type'] in ('date','datetime'):
-                   today = datetime.date.today()
-                   if d[groupby][:10] == str(today):
-                       d[groupby] = 'Today'
-                   else:
-                       d[groupby] = datetime.datetime.strptime(d[groupby][:10],'%Y-%m-%d').strftime('%B %Y')
             d['__domain'] = [(groupby,'=',alldata[d['id']][groupby] or False)] + domain
+            d['__context'] = {'group_by':groupby_list[1:]}
+            if fget.has_key(groupby):
+                if d[groupby] and fget[groupby]['type'] in ('date','datetime'):
+                   dt = datetime.datetime.strptime(alldata[d['id']][groupby][:7],'%Y-%m')
+                   days = calendar.monthrange(dt.year, dt.month)[1]
+
+                   d[groupby] = datetime.datetime.strptime(d[groupby][:10],'%Y-%m-%d').strftime('%B %Y')
+                   d['__domain'] = [(groupby,'>=',alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-01','%Y-%m-%d').strftime('%Y-%m-%d') or False),\
+                                    (groupby,'<=',alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-' + str(days),'%Y-%m-%d').strftime('%Y-%m-%d') or False)] + domain
+                elif fget[groupby]['type'] == 'many2one':
+                    d[groupby] = d[groupby] and ((type(d[groupby])==type(1)) and d[groupby] or d[groupby][1])  or ''
+
             del alldata[d['id']][groupby]
             d.update(alldata[d['id']])
             del d['id']
         return data
 
+    def _inherits_join_calc(self, field, tables, where_clause):
+        """ Adds missing table select and join clause(s) for reaching
+            the field coming from an '_inherits' parent table.
+            @param tables: list of table._table names enclosed in double quotes as returned
+                           by _where_calc()
+        """
+        current_table = self
+        while field in current_table._inherit_fields and not field in current_table._columns:
+            parent_table = self.pool.get(current_table._inherit_fields[field][0])
+            parent_table_name = parent_table._table
+            if '"%s"'%parent_table_name not in tables:
+                tables.append('"%s"'%parent_table_name)
+                where_clause.append('(%s.%s = %s.id)' % (current_table._table, current_table._inherits[parent_table._name], parent_table_name))
+            current_table = parent_table
+        return (tables, where_clause)
+
     def _parent_store_compute(self, cr):
         logger = netsvc.Logger()
         logger.notifyChannel('orm', netsvc.LOG_INFO, 'Computing parent left and right for table %s...' % (self._table, ))
@@ -1965,14 +2086,24 @@ class orm(orm_template):
 
             # iterate on the "object columns"
             todo_update_store = []
+            update_custom_fields = context.get('update_custom_fields', False)
             for k in self._columns:
                 if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'):
                     continue
                     #raise _('Can not define a column %s. Reserved keyword !') % (k,)
+                #Not Updating Custom fields
+                if k.startswith('x_') and not update_custom_fields:
+                    continue
                 f = self._columns[k]
 
                 if isinstance(f, fields.one2many):
                     cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,))
+
+                    if self.pool.get(f._obj):
+                        if f._fields_id not in self.pool.get(f._obj)._columns.keys():
+                            if not self.pool.get(f._obj)._inherits or (f._fields_id not in self.pool.get(f._obj)._inherit_fields.keys()):
+                                raise except_orm('Programming Error', ("There is no reference field '%s' found for '%s'") % (f._fields_id,f._obj,))
+
                     if cr.fetchone():
                         cr.execute("SELECT count(1) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj, f._fields_id))
                         res = cr.fetchone()[0]
@@ -2066,7 +2197,7 @@ class orm(orm_template):
                         if isinstance(f, fields.function) and not f.store and\
                                 not getattr(f, 'nodrop', False):
                             logger.notifyChannel('orm', netsvc.LOG_INFO, 'column %s (%s) in table %s removed: converted to a function !\n' % (k, f.string, self._table))
-                            cr.execute('ALTER TABLE "%s" DROP COLUMN "%s"'% (self._table, k))
+                            cr.execute('ALTER TABLE "%s" DROP COLUMN "%s" CASCADE'% (self._table, k))
                             cr.commit()
                             f_obj_type = None
                         else:
@@ -2093,19 +2224,9 @@ class orm(orm_template):
                                 cr.commit()
                             for c in casts:
                                 if (f_pg_type==c[0]) and (f._type==c[1]):
-                                    # Adding upcoming 6 lines to check whether only the size of the fields got changed or not.E.g. :(16,3) to (16,4)
-                                    field_size_change = False
-                                    if f_pg_type in ['int4','numeric','float8']:
-                                        if f.digits:
-                                            field_size = (65535 * f.digits[0]) + f.digits[0] + f.digits[1]
-                                            if field_size != f_pg_size:
-                                                field_size_change = True
-
-                                    if f_pg_type != f_obj_type or field_size_change:
+                                    if f_pg_type != f_obj_type:
                                         if f_pg_type != f_obj_type:
                                             logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed type to %s." % (k, self._table, c[1]))
-                                        if field_size_change:
-                                            logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed in the size." % (k, self._table))
                                         ok = True
                                         cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
                                         cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2]))
@@ -2171,14 +2292,7 @@ class orm(orm_template):
                                                 "AND con.contype = 'f'", (self._table, ref, k, 'id'))
                                     res = cr.dictfetchall()
                                     if res:
-                                        confdeltype = {
-                                            'RESTRICT': 'r',
-                                            'NO ACTION': 'a',
-                                            'CASCADE': 'c',
-                                            'SET NULL': 'n',
-                                            'SET DEFAULT': 'd',
-                                        }
-                                        if res[0]['confdeltype'] != confdeltype.get(f.ondelete.upper(), 'a'):
+                                        if res[0]['confdeltype'] != POSTGRES_CONFDELTYPES.get(f.ondelete.upper(), 'a'):
                                             cr.execute('ALTER TABLE "' + self._table + '" DROP CONSTRAINT "' + res[0]['conname'] + '"')
                                             cr.execute('ALTER TABLE "' + self._table + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete)
                                             cr.commit()
@@ -2222,6 +2336,8 @@ class orm(orm_template):
         self._columns = self._columns.copy()
         for store_field in self._columns:
             f = self._columns[store_field]
+            if hasattr(f, 'digits_change'):
+                f.digits_change(cr)
             if not isinstance(f, fields.function):
                 continue
             if not f.store:
@@ -2335,7 +2451,7 @@ class orm(orm_template):
                 fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2]
                 if fld_def._type in ('many2one', 'one2one'):
                     obj = self.pool.get(fld_def._obj)
-                    if not obj.search(cr, uid, [('id', '=', field_value)]):
+                    if not obj.search(cr, uid, [('id', '=', field_value or False)]):
                         continue
                 if fld_def._type in ('many2many'):
                     obj = self.pool.get(fld_def._obj)
@@ -2362,13 +2478,14 @@ class orm(orm_template):
                     field_value = field_value2
                 value[field] = field_value
         for key in context or {}:
-            if key.startswith('default_'):
+            if key.startswith('default_') and (key[8:] in fields_list):
                 value[key[8:]] = context[key]
         return value
 
     #
     # Update objects that uses this one to update their _inherits fields
     #
+
     def _inherits_reload_src(self):
         for obj in self.pool.obj_pool.values():
             if self._name in obj._inherits:
@@ -2435,8 +2552,7 @@ class orm(orm_template):
             fields_to_read = self._columns.keys()
 
         # construct a clause for the rules :
-        d1, d2, tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, context=context)
-
+        d1, d2, tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'read', context=context)
         # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
         fields_pre = [f for f in fields_to_read if
                            f == self.CONCURRENCY_CHECK_FIELD
@@ -2457,17 +2573,16 @@ class orm(orm_template):
                 return '"%s"' % (f,)
             fields_pre2 = map(convert_field, fields_pre)
             order_by = self._parent_order or self._order
-            for i in range(0, len(ids), cr.IN_MAX):
-                sub_ids = ids[i:i+cr.IN_MAX]
+            for sub_ids in cr.split_for_in_conditions(ids):
                 if d1:
-                    cr.execute('SELECT %s FROM %s WHERE %s.id = ANY (%%s) AND %s ORDER BY %s' % \
+                    cr.execute('SELECT %s FROM %s WHERE %s.id IN %%s AND %s ORDER BY %s' % \
                             (','.join(fields_pre2 + [self._table + '.id']), ','.join(tables), self._table, ' and '.join(d1),
                                 order_by),[sub_ids,]+d2)
-                    if not cr.rowcount == len({}.fromkeys(sub_ids)):
+                    if cr.rowcount != len(sub_ids):
                         raise except_orm(_('AccessError'),
                                 _('You try to bypass an access rule while reading (Document type: %s).') % self._description)
                 else:
-                    cr.execute('SELECT %s FROM \"%s\" WHERE id = ANY (%%s) ORDER BY %s' %
+                    cr.execute('SELECT %s FROM \"%s\" WHERE id IN %%s ORDER BY %s' %
                                (','.join(fields_pre2 + ['id']), self._table,
                                 order_by), (sub_ids,))
                 res.extend(cr.dictfetchall())
@@ -2497,6 +2612,8 @@ class orm(orm_template):
                 del r['id']
 
             for record in res:
+                if not record[col]:# if the record is deleted from _inherits table?
+                    continue
                 record.update(res3[record[col]])
                 if col not in fields_to_read:
                     del record[col]
@@ -2504,8 +2621,6 @@ class orm(orm_template):
         # all fields which need to be post-processed by a simple function (symbol_get)
         fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields_to_read)
         if fields_post:
-            # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
-            # to get the _symbol_get in each occurence
             for r in res:
                 for f in fields_post:
                     r[f] = self._columns[f]._symbol_get(r[f])
@@ -2619,6 +2734,25 @@ class orm(orm_template):
                     if res and res[0]:
                         raise except_orm('ConcurrencyException', _('Records were modified in the meanwhile'))
 
+    def check_access_rule(self, cr, uid, ids, operation, context=None):
+        """Verifies that the operation given by ``operation`` is allowed for the user
+           according to ir.rules.
+           @param ``operation``: one of ``'read'``, ``'write'``, ``'unlink'``
+           @raise ``except_orm``: if current ir.rules do not permit this operation.
+           @return: ``None`` if the operation is allowed
+        """
+        where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context)
+        if where_clause:
+            where_clause = ' and ' + ' and '.join(where_clause)
+            for sub_ids in cr.split_for_in_conditions(ids):
+                cr.execute('SELECT ' + self._table + '.id FROM ' + ','.join(tables) +
+                           ' WHERE ' + self._table + '.id IN %s' + where_clause,
+                           [sub_ids] + where_params)
+                if cr.rowcount != len(sub_ids):
+                    raise except_orm(_('AccessError'),
+                                     _('Operation prohibited by access rules (Operation: %s, Document type: %s).')
+                                     % (operation, self._name))
+
     def unlink(self, cr, uid, ids, context=None):
         if not ids:
             return True
@@ -2648,26 +2782,13 @@ class orm(orm_template):
         #   ids2 = [x[self._inherits[key]] for x in res]
         #   self.pool.get(key).unlink(cr, uid, ids2)
 
-        d1, d2,tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, context=context)
-        if d1:
-            d1 = ' AND '+' and '.join(d1)
-
-        for i in range(0, len(ids), cr.IN_MAX):
-            sub_ids = ids[i:i+cr.IN_MAX]
-            str_d = string.join(('%s',)*len(sub_ids), ',')
-            if d1:
-                cr.execute('SELECT '+self._table+'.id FROM '+','.join(tables)+' ' \
-                        'WHERE '+self._table+'.id IN ('+str_d+')'+d1, sub_ids+d2)
-                if not cr.rowcount == len(sub_ids):
-                    raise except_orm(_('AccessError'),
-                            _('You try to bypass an access rule (Document type: %s).') % \
-                                    self._description)
-
-            cr.execute('delete from '+self._table+' ' \
-                    'where id in ('+str_d+')', sub_ids)
+        self.check_access_rule(cr, uid, ids, 'unlink', context=context)
+        for sub_ids in cr.split_for_in_conditions(ids):
+            cr.execute('delete from ' + self._table + ' ' \
+                       'where id in %s', sub_ids)
 
         for order, object, store_ids, fields in result_store:
-            if object<>self._name:
+            if object != self._name:
                 obj =  self.pool.get(object)
                 cr.execute('select id from '+obj._table+' where id in ('+','.join(map(str, store_ids))+')')
                 rids = map(lambda x: x[0], cr.fetchall())
@@ -2763,29 +2884,10 @@ class orm(orm_template):
             upd1.append(user)
 
         if len(upd0):
-
-            d1, d2,tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, context=context)
-            if d1:
-                d1 = ' and '+' and '.join(d1)
-
-            for i in range(0, len(ids), cr.IN_MAX):
-                sub_ids = ids[i:i+cr.IN_MAX]
-                ids_str = string.join(map(str, sub_ids), ',')
-                if d1:
-                    cr.execute('SELECT '+self._table+'.id FROM '+','.join(tables)+' ' \
-                            'WHERE '+self._table+'.id IN ('+ids_str+')'+d1, d2)
-                    if not cr.rowcount == len({}.fromkeys(sub_ids)):
-                        raise except_orm(_('AccessError'),
-                                _('You try to bypass an access rule while writing (Document type: %s).') % \
-                                        self._description)
-                else:
-                    cr.execute('SELECT id FROM "'+self._table+'" WHERE id IN ('+ids_str+')')
-                    if not cr.rowcount == len({}.fromkeys(sub_ids)):
-                        raise except_orm(_('AccessError'),
-                                _('You try to write on an record that doesn\'t exist ' \
-                                        '(Document type: %s).') % self._description)
-                cr.execute('update '+self._table+' set '+string.join(upd0, ',')+' ' \
-                    'where id in ('+ids_str+')', upd1)
+            self.check_access_rule(cr, user, ids, 'write', context=context)
+            for sub_ids in cr.split_for_in_conditions(ids):
+                cr.execute('update ' + self._table + ' set ' + ','.join(upd0) + ' ' \
+                           'where id in %s', upd1 + [sub_ids])
 
             if totranslate:
                 # TODO: optimize
@@ -2816,11 +2918,9 @@ class orm(orm_template):
         for table in self._inherits:
             col = self._inherits[table]
             nids = []
-            for i in range(0, len(ids), cr.IN_MAX):
-                sub_ids = ids[i:i+cr.IN_MAX]
-                ids_str = string.join(map(str, sub_ids), ',')
+            for sub_ids in cr.split_for_in_conditions(ids):
                 cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \
-                        'where id in ('+ids_str+')', upd1)
+                           'where id in %s', (sub_ids,))
                 nids.extend([x[0] for x in cr.fetchall()])
 
             v = {}
@@ -2923,7 +3023,7 @@ class orm(orm_template):
             if self._inherits[v] not in vals:
                 tocreate[v] = {}
             else:
-                tocreate[v] = {self._inherits[v]:vals[self._inherits[v]]}
+                tocreate[v] = {'id' : vals[self._inherits[v]]}
         (upd0, upd1, upd2) = ('', '', [])
         upd_todo = []
         for v in vals.keys():
@@ -2947,10 +3047,17 @@ class orm(orm_template):
         for table in tocreate:
             if self._inherits[table] in vals:
                 del vals[self._inherits[table]]
-            id = self.pool.get(table).create(cr, user, tocreate[table])
+
+            record_id = tocreate[table].pop('id', None)
+
+            if record_id is None:
+                record_id = self.pool.get(table).create(cr, user, tocreate[table], context=context)
+            else:
+                self.pool.get(table).write(cr, user, [record_id], tocreate[table], context=context)
+
             upd0 += ','+self._inherits[table]
             upd1 += ',%s'
-            upd2.append(id)
+            upd2.append(record_id)
 
         #Start : Set bool fields to be False if they are not touched(to make search more powerful)
         bool_fields = [x for x in self._columns.keys() if self._columns[x]._type=='boolean']
@@ -2992,7 +3099,8 @@ class orm(orm_template):
                 upd1 = upd1 + ',' + self._columns[field]._symbol_set[0]
                 upd2.append(self._columns[field]._symbol_set[1](vals[field]))
             else:
-                upd_todo.append(field)
+                if not isinstance(self._columns[field], fields.related):
+                    upd_todo.append(field)
             if field in self._columns \
                     and hasattr(self._columns[field], 'selection') \
                     and vals[field]:
@@ -3016,6 +3124,15 @@ class orm(orm_template):
             upd1 += ',%s,now()'
             upd2.append(user)
         cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2))
+        d1, d2, tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'create', context=context)
+        if d1:
+            d1 = ' AND '+' AND '.join(d1)
+            cr.execute('SELECT '+self._table+'.id FROM '+','.join(tables)+' ' \
+                    'WHERE '+self._table+'.id = ' +str(id_new)+d1,d2)
+            if not cr.rowcount:
+                raise except_orm(_('AccessError'),
+                                 _('You try to bypass an access rule to create (Document type: %s).') \
+                                  % self._name)
         upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
 
         if self._parent_store:
@@ -3153,8 +3270,9 @@ class orm(orm_template):
                         upd0.append('"'+v+'"='+self._columns[v]._symbol_set[0])
                         upd1.append(self._columns[v]._symbol_set[1](value[v]))
                     upd1.append(id)
-                    cr.execute('update "' + self._table + '" set ' + \
-                        string.join(upd0, ',') + ' where id = %s', upd1)
+                    if upd0 and upd1:
+                        cr.execute('update "' + self._table + '" set ' + \
+                            string.join(upd0, ',') + ' where id = %s', upd1)
 
             else:
                 for f in val:
@@ -3222,7 +3340,7 @@ class orm(orm_template):
             context = {}
         # compute the where, order by, limit and offset clauses
         (qu1, qu2, tables) = self._where_calc(cr, user, args, context=context)
-        dom = self.pool.get('ir.rule').domain_get(cr, user, self._name, context=context)
+        dom = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'read', context=context)
         qu1 = qu1 + dom[0]
         qu2 = qu2 + dom[1]
         for t in dom[2]: