[FIX] ir_model_data : SQL_Constraint failure covered
[odoo/odoo.git] / bin / osv / orm.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 #
24 # Object relationnal mapping to postgresql module
25 #    . Hierarchical structure
26 #    . Constraints consistency, validations
27 #    . Object meta Data depends on its status
28 #    . Optimised processing by complex query (multiple actions at once)
29 #    . Default fields value
30 #    . Permissions optimisation
31 #    . Persistant object: DB postgresql
32 #    . Datas conversions
33 #    . Multi-level caching system
34 #    . 2 different inheritancies
35 #    . Fields:
36 #         - classicals (varchar, integer, boolean, ...)
37 #         - relations (one2many, many2one, many2many)
38 #         - functions
39 #
40 #
41
42 import time
43 import calendar
44 import types
45 import string
46 import netsvc
47 import re
48
49 import pickle
50
51 import fields
52 import tools
53
54 import sys
55
56 try:
57     from xml import dom, xpath
58 except ImportError:
59     sys.stderr.write("ERROR: Import xpath module\n")
60     sys.stderr.write("ERROR: Try to install the old python-xml package\n")
61     sys.stderr.write('On Ubuntu Jaunty, try this: sudo cp /usr/lib/python2.6/dist-packages/oldxml/_xmlplus/utils/boolean.so /usr/lib/python2.5/site-packages/oldxml/_xmlplus/utils\n')
62     raise
63
64 from tools.config import config
65
66 regex_order = re.compile('^([a-z0-9_]+( *desc| *asc)?( *, *|))+$', re.I)
67
68 def last_day_of_current_month():
69     import datetime
70     import calendar
71     today = datetime.date.today()
72     last_day = str(calendar.monthrange(today.year, today.month)[1])
73     return time.strftime('%Y-%m-' + last_day)
74
75 def intersect(la, lb):
76     return filter(lambda x: x in lb, la)
77
78
79 class except_orm(Exception):
80     def __init__(self, name, value):
81         self.name = name
82         self.value = value
83         self.args = (name, value)
84
85 class BrowseRecordError(Exception):
86     pass
87
88 # Readonly python database object browser
89 class browse_null(object):
90
91     def __init__(self):
92         self.id = False
93
94     def __getitem__(self, name):
95         return False
96
97     def __getattr__(self, name):
98         return False  # XXX: return self ?
99
100     def __int__(self):
101         return False
102
103     def __str__(self):
104         return ''
105
106     def __nonzero__(self):
107         return False
108
109     def __unicode__(self):
110         return u''
111
112
113 #
114 # TODO: execute an object method on browse_record_list
115 #
116 class browse_record_list(list):
117
118     def __init__(self, lst, context=None):
119         if not context:
120             context = {}
121         super(browse_record_list, self).__init__(lst)
122         self.context = context
123
124
125 class browse_record(object):
126     def __init__(self, cr, uid, id, table, cache, context=None, list_class = None, fields_process={}):
127         '''
128         table : the object (inherited from orm)
129         context : a dictionary with an optional context
130         '''
131         if not context:
132             context = {}
133         self._list_class = list_class or browse_record_list
134         self._cr = cr
135         self._uid = uid
136         self._id = id
137         self._table = table
138         self._table_name = self._table._name
139         self._context = context
140         self._fields_process = fields_process
141
142         cache.setdefault(table._name, {})
143         self._data = cache[table._name]
144
145         if not (id and isinstance(id, (int, long,))):
146             raise BrowseRecordError(_('Wrong ID for the browse record, got %r, expected an integer.') % (id,))
147 #        if not table.exists(cr, uid, id, context):
148 #            raise BrowseRecordError(_('Object %s does not exists') % (self,))
149
150         if id not in self._data:
151             self._data[id] = {'id': id}
152
153         self._cache = cache
154
155     def __getitem__(self, name):
156         if name == 'id':
157             return self._id
158         if name not in self._data[self._id]:
159             # build the list of fields we will fetch
160
161             # fetch the definition of the field which was asked for
162             if name in self._table._columns:
163                 col = self._table._columns[name]
164             elif name in self._table._inherit_fields:
165                 col = self._table._inherit_fields[name][2]
166             elif hasattr(self._table, name):
167                 if isinstance(getattr(self._table, name), (types.MethodType, types.LambdaType, types.FunctionType)):
168                     return lambda *args, **argv: getattr(self._table, name)(self._cr, self._uid, [self._id], *args, **argv)
169                 else:
170                     return getattr(self._table, name)
171             else:
172                 logger = netsvc.Logger()
173                 logger.notifyChannel('orm', netsvc.LOG_ERROR, "Programming error: field '%s' does not exist in object '%s' !" % (name, self._table._name))
174                 return False
175
176             # if the field is a classic one or a many2one, we'll fetch all classic and many2one fields
177             if col._classic_write:
178                 # gen the list of "local" (ie not inherited) fields which are classic or many2one
179                 ffields = filter(lambda x: x[1]._classic_write, self._table._columns.items())
180                 # gen the list of inherited fields
181                 inherits = map(lambda x: (x[0], x[1][2]), self._table._inherit_fields.items())
182                 # complete the field list with the inherited fields which are classic or many2one
183                 ffields += filter(lambda x: x[1]._classic_write, inherits)
184             # otherwise we fetch only that field
185             else:
186                 ffields = [(name, col)]
187             ids = filter(lambda id: name not in self._data[id], self._data.keys())
188             # read the data
189             fffields = map(lambda x: x[0], ffields)
190             datas = self._table.read(self._cr, self._uid, ids, fffields, context=self._context, load="_classic_write")
191             if self._fields_process:
192                 lang = self._context.get('lang', 'en_US') or 'en_US'
193                 lang_obj_ids = self.pool.get('res.lang').search(self._cr, self._uid,[('code','=',lang)])
194                 if not lang_obj_ids:
195                     raise Exception(_('Language with code "%s" is not defined in your system !\nDefine it through the Administration menu.') % (lang,))
196                 lang_obj = self.pool.get('res.lang').browse(self._cr, self._uid,lang_obj_ids[0])
197                 for n, f in ffields:
198                     if f._type in self._fields_process:
199                         for d in datas:
200                             d[n] = self._fields_process[f._type](d[n])
201                             if (d[n] is not None) and (d[n] is not False):
202                                 d[n].set_value(self._cr, self._uid, d[n], self, f, lang_obj)
203
204
205             # create browse records for 'remote' objects
206             for data in datas:
207                 for n, f in ffields:
208                     if f._type in ('many2one', 'one2one'):
209                         if data[n]:
210                             obj = self._table.pool.get(f._obj)
211                             compids = False
212                             if type(data[n]) in (type([]),type( (1,) )):
213                                 ids2 = data[n][0]
214                             else:
215                                 ids2 = data[n]
216                             if ids2:
217                                 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)
218                             else:
219                                 data[n] = browse_null()
220                         else:
221                             data[n] = browse_null()
222                     elif f._type in ('one2many', 'many2many') and len(data[n]):
223                         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)
224                 self._data[data['id']].update(data)
225         return self._data[self._id][name]
226
227     def __getattr__(self, name):
228 #       raise an AttributeError exception.
229         return self[name]
230
231     def __contains__(self, name):
232         return (name in self._table._columns) or (name in self._table._inherit_fields) or hasattr(self._table, name)
233
234     def __hasattr__(self, name):
235         return name in self
236
237     def __int__(self):
238         return self._id
239
240     def __str__(self):
241         return "browse_record(%s, %d)" % (self._table_name, self._id)
242
243     def __eq__(self, other):
244         return (self._table_name, self._id) == (other._table_name, other._id)
245
246     def __ne__(self, other):
247         return (self._table_name, self._id) != (other._table_name, other._id)
248
249     # we need to define __unicode__ even though we've already defined __str__
250     # because we have overridden __getattr__
251     def __unicode__(self):
252         return unicode(str(self))
253
254     def __hash__(self):
255         return hash((self._table_name, self._id))
256
257     __repr__ = __str__
258
259
260 def get_pg_type(f):
261     '''
262     returns a tuple
263     (type returned by postgres when the column was created, type expression to create the column)
264     '''
265
266     type_dict = {
267             fields.boolean: 'bool',
268             fields.integer: 'int4',
269             fields.integer_big: 'int8',
270             fields.text: 'text',
271             fields.date: 'date',
272             fields.time: 'time',
273             fields.datetime: 'timestamp',
274             fields.binary: 'bytea',
275             fields.many2one: 'int4',
276             }
277     if type(f) in type_dict:
278         f_type = (type_dict[type(f)], type_dict[type(f)])
279     elif isinstance(f, fields.float):
280         if f.digits:
281             f_type = ('numeric', 'NUMERIC(%d,%d)' % (f.digits[0], f.digits[1]))
282         else:
283             f_type = ('float8', 'DOUBLE PRECISION')
284     elif isinstance(f, (fields.char, fields.reference)):
285         f_type = ('varchar', 'VARCHAR(%d)' % (f.size,))
286     elif isinstance(f, fields.selection):
287         if isinstance(f.selection, list) and isinstance(f.selection[0][0], (str, unicode)):
288             f_size = reduce(lambda x, y: max(x, len(y[0])), f.selection, f.size or 16)
289         elif isinstance(f.selection, list) and isinstance(f.selection[0][0], int):
290             f_size = -1
291         else:
292             f_size = (hasattr(f, 'size') and f.size) or 16
293
294         if f_size == -1:
295             f_type = ('int4', 'INTEGER')
296         else:
297             f_type = ('varchar', 'VARCHAR(%d)' % f_size)
298     elif isinstance(f, fields.function) and eval('fields.'+(f._type)) in type_dict:
299         t = eval('fields.'+(f._type))
300         f_type = (type_dict[t], type_dict[t])
301     elif isinstance(f, fields.function) and f._type == 'float':
302         f_type = ('float8', 'DOUBLE PRECISION')
303     elif isinstance(f, fields.function) and f._type == 'selection':
304         f_type = ('text', 'text')
305     elif isinstance(f, fields.function) and f._type == 'char':
306         f_type = ('varchar', 'VARCHAR(%d)' % (f.size))
307     else:
308         logger = netsvc.Logger()
309         logger.notifyChannel("init", netsvc.LOG_WARNING, '%s type not supported!' % (type(f)))
310         f_type = None
311     return f_type
312
313
314 class orm_template(object):
315     _name = None
316     _columns = {}
317     _constraints = []
318     _defaults = {}
319     _rec_name = 'name'
320     _parent_name = 'parent_id'
321     _parent_store = False
322     _parent_order = False
323     _date_name = 'date'
324     _order = 'id'
325     _sequence = None
326     _description = None
327     _inherits = {}
328     _table = None
329     _invalids = set()
330
331     CONCURRENCY_CHECK_FIELD = '__last_update'
332
333     def _field_create(self, cr, context={}):
334         cr.execute("SELECT id FROM ir_model WHERE model=%s", (self._name,))
335         if not cr.rowcount:
336             cr.execute('SELECT nextval(%s)', ('ir_model_id_seq',))
337             model_id = cr.fetchone()[0]
338             cr.execute("INSERT INTO ir_model (id,model, name, info,state) VALUES (%s, %s, %s, %s, %s)", (model_id, self._name, self._description, self.__doc__, 'base'))
339         else:
340             model_id = cr.fetchone()[0]
341         if 'module' in context:
342             name_id = 'model_'+self._name.replace('.','_')
343             cr.execute('select * from ir_model_data where name=%s and res_id=%s', (name_id,model_id))
344             if not cr.rowcount:
345                 cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \
346                     (name_id, context['module'], 'ir.model', model_id)
347                 )
348
349         cr.commit()
350
351         cr.execute("SELECT * FROM ir_model_fields WHERE model=%s", (self._name,))
352         cols = {}
353         for rec in cr.dictfetchall():
354             cols[rec['name']] = rec
355
356         for (k, f) in self._columns.items():
357             vals = {
358                 'model_id': model_id,
359                 'model': self._name,
360                 'name': k,
361                 'field_description': f.string.replace("'", " "),
362                 'ttype': f._type,
363                 'relation': f._obj or 'NULL',
364                 'view_load': (f.view_load and 1) or 0,
365                 'select_level': str(f.select or 0),
366                 'readonly':(f.readonly and 1) or 0,
367                 'required':(f.required and 1) or 0,
368             }
369             if k not in cols:
370                 cr.execute('select nextval(%s)', ('ir_model_fields_id_seq',))
371                 id = cr.fetchone()[0]
372                 vals['id'] = id
373                 cr.execute("""INSERT INTO ir_model_fields (
374                     id, model_id, model, name, field_description, ttype,
375                     relation,view_load,state,select_level
376                 ) VALUES (
377                     %s,%s,%s,%s,%s,%s,%s,%s,%s,%s
378                 )""", (
379                     id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'],
380                      vals['relation'], bool(vals['view_load']), 'base',
381                     vals['select_level']
382                 ))
383                 if 'module' in context:
384                     name1 = 'field_' + self._table + '_' + k
385                     cr.execute("select name from ir_model_data where name='%s'"%(name1))
386                     if cr.fetchone():
387                         name1 = name1 + "_" + str(id)
388                     cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \
389                         (name1[:64], context['module'], 'ir.model.fields', id)
390                     )
391             else:
392                 for key, val in vals.items():
393                     if cols[k][key] != vals[key]:
394                         cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'], vals['model'], vals['name']))
395                         cr.commit()
396                         cr.execute("""UPDATE ir_model_fields SET
397                             model_id=%s, field_description=%s, ttype=%s, relation=%s,
398                             view_load=%s, select_level=%s, readonly=%s ,required=%s
399                         WHERE
400                             model=%s AND name=%s""", (
401                                 vals['model_id'], vals['field_description'], vals['ttype'],
402                                 vals['relation'], bool(vals['view_load']),
403                                 vals['select_level'], bool(vals['readonly']),bool(vals['required']), vals['model'], vals['name']
404                             ))
405                         continue
406         cr.commit()
407
408     def _auto_init(self, cr, context={}):
409         self._field_create(cr, context)
410
411     def __init__(self, cr):
412         if not self._name and not hasattr(self, '_inherit'):
413             name = type(self).__name__.split('.')[0]
414             msg = "The class %s has to have a _name attribute" % name
415
416             logger = netsvc.Logger()
417             logger.notifyChannel('orm', netsvc.LOG_ERROR, msg )
418             raise except_orm('ValueError', msg )
419
420         if not self._description:
421             self._description = self._name
422         if not self._table:
423             self._table = self._name.replace('.', '_')
424
425     def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}):
426         if not context:
427             context = {}
428         self._list_class = list_class or browse_record_list
429         cache = {}
430         # need to accepts ints and longs because ids coming from a method
431         # launched by button in the interface have a type long...
432         if isinstance(select, (int, long)):
433             return browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process)
434         elif isinstance(select, list):
435             return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context)
436         else:
437             return browse_null()
438
439     def __export_row(self, cr, uid, row, fields, context=None):
440         
441         def check_type(field_type):
442             if field_type == 'float':
443                 return 0.0
444             elif field_type == 'integer':
445                 return 0
446             elif field_type == 'boolean':   
447                 return False                                
448             return ''
449         
450         lines = []
451         data = map(lambda x: '', range(len(fields)))
452         done = []
453         for fpos in range(len(fields)):
454             f = fields[fpos]            
455             if f:
456                 r = row
457                 i = 0
458                 while i < len(f):
459                     if f[i] == 'db_id':
460                         r = r['id']                        
461                     elif f[i] == 'id':                        
462                         model_data = self.pool.get('ir.model.data')
463                         data_ids = model_data.search(cr, uid, [('model','=',r._table_name),('res_id','=',r['id'])])
464                         if len(data_ids):
465                             d = model_data.read(cr, uid, data_ids, ['name','module'])[0]
466                             if d['module']:
467                                 r = '%s.%s'%(d['module'],d['name'])
468                             else:
469                                 r = d['name']
470                         else:
471                             break
472                     else:
473                         r = r[f[i]]                   
474                     if not r:
475                         if f[i] in self._columns: 
476                             r = check_type(self._columns[f[i]]._type)
477                         elif f[i] in self._inherit_fields:
478                             r = check_type(self._inherit_fields[f[i]][2]._type)                        
479                         data[fpos] = r                        
480                         break
481                     if isinstance(r, (browse_record_list, list)):
482                         first = True
483                         fields2 = map(lambda x: (x[:i+1]==f[:i+1] and x[i+1:]) \
484                                 or [], fields)
485                         if fields2 in done:
486                             break
487                         done.append(fields2)                        
488                         for row2 in r:
489                             lines2 = self.__export_row(cr, uid, row2, fields2,
490                                     context)                            
491                             if first:
492                                 for fpos2 in range(len(fields)):
493                                     if lines2 and lines2[0][fpos2]:
494                                         data[fpos2] = lines2[0][fpos2]
495                                 if not data[fpos]:
496                                     dt = ''
497                                     for rr in r :
498                                         if isinstance(rr.name, browse_record):
499                                             rr = rr.name
500                                         dt+=rr.name+','
501                                     data[fpos] = dt[:-1]
502                                     break
503                                 lines += lines2[1:]
504                                 first = False
505                             else:
506                                 lines += lines2                            
507                         break
508                     i += 1
509                 if i == len(f):
510                     if isinstance(r, browse_record):
511                         r = r.name
512                     data[fpos] = tools.ustr(r or '')
513         return [data] + lines
514
515     def export_data(self, cr, uid, ids, fields_to_export, context=None):
516         if not context:
517             context = {}
518         imp_comp = context.get('import_comp',False)        
519         cols = self._columns.copy()
520         for f in self._inherit_fields:
521             cols.update({f: self._inherit_fields[f][2]})        
522         fields_to_export = map(lambda x: x.split('/'), fields_to_export)
523         fields_export = fields_to_export+[]        
524         warning = ''  
525         warning_fields = []      
526         for field in fields_export:
527             if imp_comp and len(field)>1:                              
528                 warning_fields.append('/'.join(map(lambda x:x in cols and cols[x].string or x,field)))
529             elif len (field) <=1:
530                 if imp_comp and cols.get(field and field[0],False):
531                     if ((isinstance(cols[field[0]], fields.function) and not cols[field[0]].store) \
532                                      or isinstance(cols[field[0]], fields.related)\
533                                      or isinstance(cols[field[0]], fields.one2many)):                        
534                         warning_fields.append('/'.join(map(lambda x:x in cols and cols[x].string or x,field)))
535         datas = []
536         if imp_comp and len(warning_fields):
537             warning = 'Following columns cannot be exported since you select to be import compatible.\n%s' %('\n'.join(warning_fields))        
538             cr.rollback()
539             return {'warning' : warning}
540         for row in self.browse(cr, uid, ids, context):
541             datas += self.__export_row(cr, uid, row, fields_to_export, context)
542         return {'datas':datas}
543
544     def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None):
545         if not context:
546             context = {}
547         fields = map(lambda x: x.split('/'), fields)
548         logger = netsvc.Logger()
549         ir_model_data_obj = self.pool.get('ir.model.data')
550         def process_liness(self, datas, prefix, current_module, model_name, fields_def, position=0):
551             line = datas[position]
552             row = {}
553             translate = {}
554             todo = []
555             warning = ''
556             data_id = False
557             data_res_id = False
558             is_xml_id = False
559             is_db_id = False
560             ir_model_data_obj = self.pool.get('ir.model.data')
561             #
562             # Import normal fields
563             #
564             for i in range(len(fields)):
565                 if i >= len(line):
566                     raise Exception(_('Please check that all your lines have %d columns.') % (len(fields),))
567                 if not line[i]:
568                     continue
569                 field = fields[i]                
570                 if (len(field)==len(prefix)+1) and field[len(prefix)].endswith(':id'):
571                     res_id = False
572                     if line[i]:
573                         if fields_def[field[len(prefix)][:-3]]['type']=='many2many':
574                             res_id = []
575                             for word in line[i].split(config.get('csv_internal_sep')):
576                                 if '.' in word:
577                                     module, xml_id = word.rsplit('.', 1)
578                                 else:
579                                     module, xml_id = current_module, word                                
580                                 id = ir_model_data_obj._get_id(cr, uid, module,
581                                         xml_id)
582                                 res_id2 = ir_model_data_obj.read(cr, uid, [id],
583                                         ['res_id'])[0]['res_id']
584                                 if res_id2:
585                                     res_id.append(res_id2)
586                             if len(res_id):
587                                 res_id = [(6, 0, res_id)]
588                         else:
589                             if '.' in line[i]:
590                                 module, xml_id = line[i].rsplit('.', 1)
591                             else:
592                                 module, xml_id = current_module, line[i]                            
593                             id = ir_model_data_obj._get_id(cr, uid, module, xml_id)
594                             res_id = ir_model_data_obj.read(cr, uid, [id],
595                                     ['res_id'])[0]['res_id']
596                     row[field[0][:-3]] = res_id or False
597                     continue
598                 if (len(field) == len(prefix)+1) and \
599                         len(field[len(prefix)].split(':lang=')) == 2:
600                     f, lang = field[len(prefix)].split(':lang=')
601                     translate.setdefault(lang, {})[f]=line[i] or False
602                     continue
603                 if (len(field) == len(prefix)+1) and \
604                         (prefix == field[0:len(prefix)]):
605                     if field[len(prefix)] == "id":  
606                         # XML ID                         
607                         db_id = False                 
608                         is_xml_id = data_id = line[i] 
609                         d =  data_id.split('.')
610                         module = len(d)>1 and d[0] or ''
611                         name = len(d)>1 and d[1] or d[0] 
612                         data_ids = ir_model_data_obj.search(cr, uid, [('module','=',module),('model','=',model_name),('name','=',name)])                    
613                         if len(data_ids):
614                             d = ir_model_data_obj.read(cr, uid, data_ids, ['res_id'])[0]                                                
615                             db_id = d['res_id']                       
616                         if is_db_id and not db_id:
617                            data_ids = ir_model_data_obj.search(cr, uid, [('module','=',module),('model','=',model_name),('res_id','=',is_db_id)])                     
618                            if not len(data_ids):
619                                ir_model_data_obj.create(cr, uid, {'module':module, 'model':model_name, 'name':name, 'res_id':is_db_id}) 
620                                db_id = is_db_id 
621                         if is_db_id and int(db_id) != int(is_db_id):                        
622                             warning += ("Id is not the same than existing one: " + str(is_db_id) + " !\n")
623                             logger.notifyChannel("import", netsvc.LOG_ERROR,
624                                     "Id is not the same than existing one: " + str(is_db_id) + ' !\n')
625                         continue
626
627                     if field[len(prefix)] == "db_id":
628                         # Database ID                        
629                         try:                                        
630                             line[i]= int(line[i])                    
631                         except Exception, e:
632                             warning += (str(e) +  "!\n")
633                             logger.notifyChannel("import", netsvc.LOG_ERROR,
634                                     str(e)  + '!\n')
635                             continue
636                         is_db_id = line[i]
637                         obj_model = self.pool.get(model_name)                        
638                         ids = obj_model.search(cr, uid, [('id','=',line[i])])
639                         if not len(ids):
640                             warning += ("Database ID doesn't exist: " + model_name + ": " + str(line[i]) + " !\n")
641                             logger.notifyChannel("import", netsvc.LOG_ERROR,
642                                     "Database ID doesn't exist: " + model_name + ": " + str(line[i]) + ' !\n')
643                             continue
644                         else:
645                             data_res_id = ids[0]
646                         data_ids = ir_model_data_obj.search(cr, uid, [('model','=',model_name),('res_id','=',line[i])])
647                         if len(data_ids):
648                             d = ir_model_data_obj.read(cr, uid, data_ids, ['name','module'])[0]                                                
649                             data_id = d['name']       
650                             if d['module']:
651                                 data_id = '%s.%s'%(d['module'],d['name'])
652                             else:
653                                 data_id = d['name']
654                         if is_xml_id and not data_id:
655                             data_id = is_xml_id                                     
656                         if is_xml_id and is_xml_id!=data_id:  
657                             warning += ("Id is not the same than existing one: " + str(line[i]) + " !\n")
658                             logger.notifyChannel("import", netsvc.LOG_ERROR,
659                                     "Id is not the same than existing one: " + str(line[i]) + ' !\n')  
660                                                            
661                         continue
662                     if fields_def[field[len(prefix)]]['type'] == 'integer':
663                         res = line[i] and int(line[i])
664                     elif fields_def[field[len(prefix)]]['type'] == 'boolean':
665                         res = line[i] and eval(line[i])
666                     elif fields_def[field[len(prefix)]]['type'] == 'float':
667                         res = line[i] and float(line[i])
668                     elif fields_def[field[len(prefix)]]['type'] == 'selection':
669                         res = False
670                         if isinstance(fields_def[field[len(prefix)]]['selection'],
671                                 (tuple, list)):
672                             sel = fields_def[field[len(prefix)]]['selection']
673                         else:
674                             sel = fields_def[field[len(prefix)]]['selection'](self,
675                                     cr, uid, context)
676                         for key, val in sel:
677                             if line[i] in [tools.ustr(key),tools.ustr(val)]: #Acepting key or value for selection field
678                                 res = key
679                                 break
680                         if line[i] and not res:
681                             logger.notifyChannel("import", netsvc.LOG_WARNING,
682                                     "key '%s' not found in selection field '%s'" % \
683                                             (line[i], field[len(prefix)]))
684                             
685                             warning += "Key/value '"+ str(line[i]) +"' not found in selection field '"+str(field[len(prefix)])+"'"
686                             
687                     elif fields_def[field[len(prefix)]]['type']=='many2one':
688                         res = False
689                         if line[i]:
690                             relation = fields_def[field[len(prefix)]]['relation']
691                             res2 = self.pool.get(relation).name_search(cr, uid,
692                                     line[i], [], operator='=', context=context)
693                             res = (res2 and res2[0][0]) or False
694                             if not res:
695                                 warning += ('Relation not found: ' + line[i] + \
696                                         ' on ' + relation + ' !\n')
697                                 logger.notifyChannel("import", netsvc.LOG_WARNING,
698                                         'Relation not found: ' + line[i] + \
699                                                 ' on ' + relation + ' !\n')
700                     elif fields_def[field[len(prefix)]]['type']=='many2many':
701                         res = []
702                         if line[i]:
703                             relation = fields_def[field[len(prefix)]]['relation']
704                             for word in line[i].split(config.get('csv_internal_sep')):
705                                 res2 = self.pool.get(relation).name_search(cr,
706                                         uid, word, [], operator='=', context=context)
707                                 res3 = (res2 and res2[0][0]) or False
708                                 if not res3:
709                                     warning += ('Relation not found: ' + \
710                                             line[i] + ' on '+relation + ' !\n')
711                                     logger.notifyChannel("import",
712                                             netsvc.LOG_WARNING,
713                                             'Relation not found: ' + line[i] + \
714                                                     ' on '+relation + ' !\n')
715                                 else:
716                                     res.append(res3)
717                             if len(res):
718                                 res = [(6, 0, res)]
719                     else:
720                         res = line[i] or False
721                     row[field[len(prefix)]] = res
722                 elif (prefix==field[0:len(prefix)]):
723                     if field[0] not in todo:
724                         todo.append(field[len(prefix)])
725             #
726             # Import one2many, many2many fields
727             #
728             nbrmax = 1
729             for field in todo:
730                 relation_obj = self.pool.get(fields_def[field]['relation'])
731                 newfd = relation_obj.fields_get(
732                         cr, uid, context=context)
733                 res = process_liness(self, datas, prefix + [field], current_module, relation_obj._name, newfd, position)                              
734                 (newrow, max2, w2, translate2, data_id2, data_res_id2) = res                  
735                 nbrmax = max(nbrmax, max2)
736                 warning = warning + w2         
737                 reduce(lambda x, y: x and y, newrow)       
738                 row[field] = (reduce(lambda x, y: x or y, newrow.values()) and \
739                         [(0, 0, newrow)]) or []                
740                 i = max2
741                 while (position+i)<len(datas):
742                     ok = True
743                     for j in range(len(fields)):
744                         field2 = fields[j]
745                         if (len(field2) <= (len(prefix)+1)) and datas[position+i][j]:
746                             ok = False
747                     if not ok:
748                         break
749
750                     (newrow, max2, w2, translate2, data_id2, data_res_id2) = process_liness(
751                             self, datas, prefix+[field], current_module, relation_obj._name, newfd, position+i)
752                     warning = warning+w2
753                     if reduce(lambda x, y: x or y, newrow.values()):
754                         row[field].append((0, 0, newrow))                    
755                     i += max2
756                     nbrmax = max(nbrmax, i)
757
758             if len(prefix)==0:
759                 for i in range(max(nbrmax, 1)):
760                     #if datas:
761                     datas.pop(0)
762             result = (row, nbrmax, warning, translate, data_id, data_res_id)
763             return result
764
765         fields_def = self.fields_get(cr, uid, context=context)
766         done = 0
767
768         initial_size = len(datas)
769         if config.get('import_partial', False) and filename:
770             data = pickle.load(file(config.get('import_partial')))
771             original_value =  data.get(filename, 0)
772         counter = 0
773         while len(datas):
774             counter += 1
775             res = {}
776             #try:
777             (res, other, warning, translate, data_id, res_id) = \
778                     process_liness(self, datas, [], current_module, self._name, fields_def)
779             if warning:                        
780                 cr.rollback()
781                 return (-1, res, 'Line ' + str(counter) +' : ' + warning, '')
782             
783             try:
784                 id = ir_model_data_obj._update(cr, uid, self._name,
785                      current_module, res, xml_id=data_id, mode=mode,
786                      noupdate=noupdate, res_id=res_id)
787             except Exception, e:
788                 import psycopg2
789                 if isinstance(e,psycopg2.IntegrityError):
790                     msg= 'Insertion Failed!'
791                     for key in self.pool._sql_error.keys():
792                         if key in e[0]:
793                             msg = self.pool._sql_error[key]
794                             break
795                     return (-1, res,'Line ' + str(counter) +' : ' + msg,'' )
796                 
797             for lang in translate:
798                 context2 = context.copy()
799                 context2['lang'] = lang
800                 self.write(cr, uid, [id], translate[lang], context2)
801             if config.get('import_partial', False) and filename and (not (counter%100)) :
802                 data = pickle.load(file(config.get('import_partial')))
803                 data[filename] = initial_size - len(datas) + original_value
804                 pickle.dump(data, file(config.get('import_partial'),'wb'))
805                 cr.commit()
806
807             #except Exception, e:
808             #    logger.notifyChannel("import", netsvc.LOG_ERROR, e)
809             #    cr.rollback()
810             #    try:
811             #        return (-1, res, e[0], warning)
812             #    except:
813             #        return (-1, res, e[0], '')
814             done += 1
815         #
816         # TODO: Send a request with the result and multi-thread !
817         #
818         return (done, 0, 0, 0)
819
820     def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
821         raise _('The read method is not implemented on this object !')
822
823     def get_invalid_fields(self,cr,uid):
824         return list(self._invalids)
825
826     def _validate(self, cr, uid, ids, context=None):
827         context = context or {}
828         lng = context.get('lang', False) or 'en_US'
829         trans = self.pool.get('ir.translation')
830         error_msgs = []
831         for constraint in self._constraints:
832             fun, msg, fields = constraint
833             if not fun(self, cr, uid, ids):
834                 translated_msg = trans._get_source(cr, uid, self._name, 'constraint', lng, source=msg) or msg
835                 error_msgs.append(
836                         _("Error occurred while validating the field(s) %s: %s") % (','.join(fields), translated_msg)
837                 )
838                 self._invalids.update(fields)
839         if error_msgs:
840             cr.rollback()
841             raise except_orm('ValidateError', '\n'.join(error_msgs))
842         else:
843             self._invalids.clear()
844
845     def default_get(self, cr, uid, fields_list, context=None):
846         return {}
847
848     def perm_read(self, cr, user, ids, context=None, details=True):
849         raise _('The perm_read method is not implemented on this object !')
850
851     def unlink(self, cr, uid, ids, context=None):
852         raise _('The unlink method is not implemented on this object !')
853
854     def write(self, cr, user, ids, vals, context=None):
855         raise _('The write method is not implemented on this object !')
856
857     def create(self, cr, user, vals, context=None):
858         raise _('The create method is not implemented on this object !')
859
860     # returns the definition of each field in the object
861     # the optional fields parameter can limit the result to some fields
862     def fields_get_keys(self, cr, user, context=None, read_access=True):
863         if context is None:
864             context = {}
865         res = self._columns.keys()
866         for parent in self._inherits:
867             res.extend(self.pool.get(parent).fields_get_keys(cr, user, fields, context))
868         return res
869
870     def fields_get(self, cr, user, fields=None, context=None, read_access=True):
871         if context is None:
872             context = {}
873         res = {}
874         translation_obj = self.pool.get('ir.translation')
875         model_access_obj = self.pool.get('ir.model.access')
876         for parent in self._inherits:
877             res.update(self.pool.get(parent).fields_get(cr, user, fields, context))
878
879         if self._columns.keys():
880             for f in self._columns.keys():
881                 if fields and f not in fields:
882                     continue
883                 res[f] = {'type': self._columns[f]._type}
884                 for arg in ('string', 'readonly', 'states', 'size', 'required',
885                         'change_default', 'translate', 'help', 'select'):
886                     if getattr(self._columns[f], arg):
887                         res[f][arg] = getattr(self._columns[f], arg)
888                 if not read_access:
889                     res[f]['readonly'] = True
890                     res[f]['states'] = {}
891                 for arg in ('digits', 'invisible','filters'):
892                     if hasattr(self._columns[f], arg) \
893                             and getattr(self._columns[f], arg):
894                         res[f][arg] = getattr(self._columns[f], arg)
895
896                 res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US')
897                 if res_trans:
898                     res[f]['string'] = res_trans
899                 help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
900                 if help_trans:
901                     res[f]['help'] = help_trans
902
903                 if hasattr(self._columns[f], 'selection'):
904                     if isinstance(self._columns[f].selection, (tuple, list)):
905                         sel = self._columns[f].selection
906                         # translate each selection option
907                         sel2 = []
908                         for (key, val) in sel:
909                             val2 = None
910                             if val:
911                                 val2 = translation_obj._get_source(cr, user, self._name + ',' + f, 'selection', context.get('lang', False) or 'en_US', val)
912                             sel2.append((key, val2 or val))
913                         sel = sel2
914                         res[f]['selection'] = sel
915                     else:
916                         # call the 'dynamic selection' function
917                         res[f]['selection'] = self._columns[f].selection(self, cr,
918                                 user, context)
919                 if res[f]['type'] in ('one2many', 'many2many', 'many2one', 'one2one'):
920                     res[f]['relation'] = self._columns[f]._obj
921                     res[f]['domain'] = self._columns[f]._domain
922                     res[f]['context'] = self._columns[f]._context
923         else:
924             #TODO : read the fields from the database
925             pass
926
927         if fields:
928             # filter out fields which aren't in the fields list
929             for r in res.keys():
930                 if r not in fields:
931                     del res[r]
932         return res
933
934     #
935     # Overload this method if you need a window title which depends on the context
936     #
937     def view_header_get(self, cr, user, view_id=None, view_type='form', context=None):
938         return False
939
940     def __view_look_dom(self, cr, user, node, view_id, context=None):
941         if not context:
942             context = {}
943         result = False
944         fields = {}
945         childs = True
946
947         if node.nodeType == node.ELEMENT_NODE and node.localName == 'field':
948             if node.hasAttribute('name'):
949                 attrs = {}
950                 try:
951                     if node.getAttribute('name') in self._columns:
952                         column = self._columns[node.getAttribute('name')]
953                     else:
954                         column = self._inherit_fields[node.getAttribute('name')][2]
955                 except:
956                     column = False
957
958                 if column:
959                     relation = column._obj
960                     childs = False
961                     views = {}
962                     for f in node.childNodes:
963                         if f.nodeType == f.ELEMENT_NODE and f.localName in ('form', 'tree', 'graph'):
964                             node.removeChild(f)
965                             ctx = context.copy()
966                             ctx['base_model_name'] = self._name
967                             xarch, xfields = self.pool.get(relation).__view_look_dom_arch(cr, user, f, view_id, ctx)
968                             views[str(f.localName)] = {
969                                 'arch': xarch,
970                                 'fields': xfields
971                             }
972                     attrs = {'views': views}
973                     if node.hasAttribute('widget') and node.getAttribute('widget')=='selection':
974                         # We can not use the 'string' domain has it is defined according to the record !
975                         dom = None
976                         if column._domain and not isinstance(column._domain, (str, unicode)):
977                             dom = column._domain
978                         attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', dom, context=context)
979                         if (node.hasAttribute('required') and not int(node.getAttribute('required'))) or not column.required:
980                             attrs['selection'].append((False,''))
981                 fields[node.getAttribute('name')] = attrs
982
983         elif node.nodeType==node.ELEMENT_NODE and node.localName in ('form', 'tree'):
984             result = self.view_header_get(cr, user, False, node.localName, context)
985             if result:
986                 node.setAttribute('string', result)
987
988         elif node.nodeType==node.ELEMENT_NODE and node.localName == 'calendar':
989             for additional_field in ('date_start', 'date_delay', 'date_stop', 'color'):
990                 if node.hasAttribute(additional_field) and node.getAttribute(additional_field):
991                     fields[node.getAttribute(additional_field)] = {}
992
993         if node.nodeType == node.ELEMENT_NODE and node.hasAttribute('groups'):
994             if node.getAttribute('groups'):
995                 groups = node.getAttribute('groups').split(',')
996                 readonly = False
997                 access_pool = self.pool.get('ir.model.access')
998                 for group in groups:
999                     readonly = readonly or access_pool.check_groups(cr, user, group)
1000                 if not readonly:
1001                     node.setAttribute('invisible', '1')
1002             node.removeAttribute('groups')
1003
1004         if node.nodeType == node.ELEMENT_NODE:
1005             # translate view
1006             if ('lang' in context) and not result:
1007                 if node.hasAttribute('string') and node.getAttribute('string'):
1008                     trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.getAttribute('string').encode('utf8'))
1009                     if not trans and ('base_model_name' in context):
1010                         trans = self.pool.get('ir.translation')._get_source(cr, user, context['base_model_name'], 'view', context['lang'], node.getAttribute('string').encode('utf8'))
1011                     if trans:
1012                         node.setAttribute('string', trans)
1013                 if node.hasAttribute('sum') and node.getAttribute('sum'):
1014                     trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.getAttribute('sum').encode('utf8'))
1015                     if trans:
1016                         node.setAttribute('sum', trans)
1017
1018         if childs:
1019             for f in node.childNodes:
1020                 fields.update(self.__view_look_dom(cr, user, f, view_id, context))
1021
1022         return fields
1023
1024     def __view_look_dom_arch(self, cr, user, node, view_id, context=None):
1025         fields_def = self.__view_look_dom(cr, user, node, view_id, context=context)
1026
1027         rolesobj = self.pool.get('res.roles')
1028         usersobj = self.pool.get('res.users')
1029
1030         buttons = (n for n in node.getElementsByTagName('button') if n.getAttribute('type') != 'object')
1031         for button in buttons:
1032             ok = True
1033             if user != 1:   # admin user has all roles
1034                 user_roles = usersobj.read(cr, user, [user], ['roles_id'])[0]['roles_id']
1035                 cr.execute("select role_id from wkf_transition where signal=%s", (button.getAttribute('name'),))
1036                 roles = cr.fetchall()
1037                 for role in roles:
1038                     if role[0]:
1039                         ok = ok and rolesobj.check(cr, user, user_roles, role[0])
1040
1041             if not ok:
1042                 button.setAttribute('readonly', '1')
1043             else:
1044                 button.setAttribute('readonly', '0')
1045
1046         arch = node.toxml(encoding="utf-8").replace('\t', '')
1047         fields = self.fields_get(cr, user, fields_def.keys(), context)
1048         for field in fields_def:
1049             if field == 'id':
1050                 # sometime, the view may containt the (invisible) field 'id' needed for a domain (when 2 objects have cross references)
1051                 fields['id'] = {'readonly': True, 'type': 'integer', 'string': 'ID'}
1052             elif field in fields:
1053                 fields[field].update(fields_def[field])
1054             else:
1055                 cr.execute('select name, model from ir_ui_view where (id=%s or inherit_id=%s) and arch like %s', (view_id, view_id, '%%%s%%' % field))
1056                 res = cr.fetchall()[:]
1057                 model = res[0][1]
1058                 res.insert(0, ("Can't find field '%s' in the following view parts composing the view of object model '%s':" % (field, model), None))
1059                 msg = "\n * ".join([r[0] for r in res])
1060                 msg += "\n\nEither you wrongly customised this view, or some modules bringing those views are not compatible with your current data model"
1061                 netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, msg)
1062                 raise except_orm('View error', msg)
1063
1064         return arch, fields
1065
1066     def __get_default_calendar_view(self):
1067         """Generate a default calendar view (For internal use only).
1068         """
1069
1070         arch = ('<?xml version="1.0" encoding="utf-8"?>\n'
1071                 '<calendar string="%s" date_start="%s"') % (self._description, self._date_name)
1072
1073         if 'user_id' in self._columns:
1074             arch += ' color="user_id"'
1075
1076         elif 'partner_id' in self._columns:
1077             arch += ' color="partner_id"'
1078
1079         if 'date_stop' in self._columns:
1080             arch += ' date_stop="date_stop"'
1081
1082         elif 'date_end' in self._columns:
1083             arch += ' date_stop="date_end"'
1084
1085         elif 'date_delay' in self._columns:
1086             arch += ' date_delay="date_delay"'
1087
1088         elif 'planned_hours' in self._columns:
1089             arch += ' date_delay="planned_hours"'
1090
1091         arch += ('>\n'
1092                  '  <field name="%s"/>\n'
1093                  '</calendar>') % (self._rec_name)
1094
1095         return arch
1096
1097     #
1098     # if view_id, view_type is not required
1099     #
1100     def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False):
1101         if not context:
1102             context = {}
1103
1104         def encode(s):
1105             if isinstance(s, unicode):
1106                 return s.encode('utf8')
1107             return s
1108
1109         def _inherit_apply(src, inherit):
1110             def _find(node, node2):
1111                 if node2.nodeType == node2.ELEMENT_NODE and node2.localName == 'xpath':
1112                     res = xpath.Evaluate(node2.getAttribute('expr'), node)
1113                     return res and res[0]
1114                 else:
1115                     if node.nodeType == node.ELEMENT_NODE and node.localName == node2.localName:
1116                         res = True
1117                         for attr in node2.attributes.keys():
1118                             if attr == 'position':
1119                                 continue
1120                             if node.hasAttribute(attr):
1121                                 if node.getAttribute(attr)==node2.getAttribute(attr):
1122                                     continue
1123                             res = False
1124                         if res:
1125                             return node
1126                     for child in node.childNodes:
1127                         res = _find(child, node2)
1128                         if res:
1129                             return res
1130                 return None
1131
1132
1133             doc_src = dom.minidom.parseString(encode(src))
1134             doc_dest = dom.minidom.parseString(encode(inherit))
1135             toparse = doc_dest.childNodes
1136             while len(toparse):
1137                 node2 = toparse.pop(0)
1138                 if not node2.nodeType == node2.ELEMENT_NODE:
1139                     continue
1140                 if node2.localName == 'data':
1141                     toparse += node2.childNodes
1142                     continue
1143                 node = _find(doc_src, node2)
1144                 if node:
1145                     pos = 'inside'
1146                     if node2.hasAttribute('position'):
1147                         pos = node2.getAttribute('position')
1148                     if pos == 'replace':
1149                         parent = node.parentNode
1150                         for child in node2.childNodes:
1151                             if child.nodeType == child.ELEMENT_NODE:
1152                                 parent.insertBefore(child, node)
1153                         parent.removeChild(node)
1154                     else:
1155                         sib = node.nextSibling
1156                         for child in node2.childNodes:
1157                             if child.nodeType == child.ELEMENT_NODE:
1158                                 if pos == 'inside':
1159                                     node.appendChild(child)
1160                                 elif pos == 'after':
1161                                     node.parentNode.insertBefore(child, sib)
1162                                 elif pos=='before':
1163                                     node.parentNode.insertBefore(child, node)
1164                                 else:
1165                                     raise AttributeError(_('Unknown position in inherited view %s !') % pos)
1166                 else:
1167                     attrs = ''.join([
1168                         ' %s="%s"' % (attr, node2.getAttribute(attr))
1169                         for attr in node2.attributes.keys()
1170                         if attr != 'position'
1171                     ])
1172                     tag = "<%s%s>" % (node2.localName, attrs)
1173                     raise AttributeError(_("Couldn't find tag '%s' in parent view !") % tag)
1174             return doc_src.toxml(encoding="utf-8").replace('\t', '')
1175
1176         result = {'type': view_type, 'model': self._name}
1177
1178         ok = True
1179         model = True
1180         sql_res = False
1181         while ok:
1182             if view_id:
1183                 where = (model and (" and model='%s'" % (self._name,))) or ''
1184                 cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE id=%s'+where, (view_id,))
1185             else:
1186                 cr.execute('''SELECT
1187                         arch,name,field_parent,id,type,inherit_id
1188                     FROM
1189                         ir_ui_view
1190                     WHERE
1191                         model=%s AND
1192                         type=%s AND
1193                         inherit_id IS NULL
1194                     ORDER BY priority''', (self._name, view_type))
1195             sql_res = cr.fetchone()
1196             if not sql_res:
1197                 break
1198             ok = sql_res[5]
1199             view_id = ok or sql_res[3]
1200             model = False
1201
1202         # if a view was found
1203         if sql_res:
1204             result['type'] = sql_res[4]
1205             result['view_id'] = sql_res[3]
1206             result['arch'] = sql_res[0]
1207
1208             def _inherit_apply_rec(result, inherit_id):
1209                 # get all views which inherit from (ie modify) this view
1210                 cr.execute('select arch,id from ir_ui_view where inherit_id=%s and model=%s order by priority', (inherit_id, self._name))
1211                 sql_inherit = cr.fetchall()
1212                 for (inherit, id) in sql_inherit:
1213                     result = _inherit_apply(result, inherit)
1214                     result = _inherit_apply_rec(result, id)
1215                 return result
1216
1217             result['arch'] = _inherit_apply_rec(result['arch'], sql_res[3])
1218
1219             result['name'] = sql_res[1]
1220             result['field_parent'] = sql_res[2] or False
1221         else:
1222             # otherwise, build some kind of default view
1223             if view_type == 'form':
1224                 res = self.fields_get(cr, user, context=context)
1225                 xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
1226                 '''<form string="%s">''' % (self._description,)
1227                 for x in res:
1228                     if res[x]['type'] not in ('one2many', 'many2many'):
1229                         xml += '<field name="%s"/>' % (x,)
1230                         if res[x]['type'] == 'text':
1231                             xml += "<newline/>"
1232                 xml += "</form>"
1233             elif view_type == 'tree':
1234                 _rec_name = self._rec_name
1235                 if _rec_name not in self._columns:
1236                     _rec_name = self._columns.keys()[0]
1237                 xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
1238                 '''<tree string="%s"><field name="%s"/></tree>''' \
1239                 % (self._description, self._rec_name)
1240             elif view_type == 'calendar':
1241                 xml = self.__get_default_calendar_view()
1242             else:
1243                 xml = ''
1244             result['arch'] = xml
1245             result['name'] = 'default'
1246             result['field_parent'] = False
1247             result['view_id'] = 0
1248
1249         doc = dom.minidom.parseString(encode(result['arch']))
1250         xarch, xfields = self.__view_look_dom_arch(cr, user, doc, view_id, context=context)
1251         result['arch'] = xarch
1252         result['fields'] = xfields
1253         if toolbar:
1254             def clean(x):
1255                 x = x[2]
1256                 for key in ('report_sxw_content', 'report_rml_content',
1257                         'report_sxw', 'report_rml',
1258                         'report_sxw_content_data', 'report_rml_content_data'):
1259                     if key in x:
1260                         del x[key]
1261                 return x
1262             ir_values_obj = self.pool.get('ir.values')
1263             resprint = ir_values_obj.get(cr, user, 'action',
1264                     'client_print_multi', [(self._name, False)], False,
1265                     context)
1266             resaction = ir_values_obj.get(cr, user, 'action',
1267                     'client_action_multi', [(self._name, False)], False,
1268                     context)
1269
1270             resrelate = ir_values_obj.get(cr, user, 'action',
1271                     'client_action_relate', [(self._name, False)], False,
1272                     context)
1273             resprint = map(clean, resprint)
1274             resaction = map(clean, resaction)
1275             resaction = filter(lambda x: not x.get('multi', False), resaction)
1276             resprint = filter(lambda x: not x.get('multi', False), resprint)
1277             resrelate = map(lambda x: x[2], resrelate)
1278
1279             for x in resprint+resaction+resrelate:
1280                 x['string'] = x['name']
1281
1282             result['toolbar'] = {
1283                 'print': resprint,
1284                 'action': resaction,
1285                 'relate': resrelate
1286             }
1287         return result
1288
1289     _view_look_dom_arch = __view_look_dom_arch
1290
1291     def search_count(self, cr, user, args, context=None):
1292         if not context:
1293             context = {}
1294         res = self.search(cr, user, args, context=context, count=True)
1295         if isinstance(res, list):
1296             return len(res)
1297         return res
1298
1299     def search(self, cr, user, args, offset=0, limit=None, order=None,
1300             context=None, count=False):
1301         raise _('The search method is not implemented on this object !')
1302
1303     def name_get(self, cr, user, ids, context=None):
1304         raise _('The name_get method is not implemented on this object !')
1305
1306     def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=None):
1307         raise _('The name_search method is not implemented on this object !')
1308
1309     def copy(self, cr, uid, id, default=None, context=None):
1310         raise _('The copy method is not implemented on this object !')
1311
1312     def exists(self, cr, uid, id, context=None):
1313         raise _('The exists method is not implemented on this object !')
1314
1315     def read_string(self, cr, uid, id, langs, fields=None, context=None):
1316         res = {}
1317         res2 = {}
1318         self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'read')
1319         if not fields:
1320             fields = self._columns.keys() + self._inherit_fields.keys()
1321         for lang in langs:
1322             res[lang] = {'code': lang}
1323             for f in fields:
1324                 if f in self._columns:
1325                     res_trans = self.pool.get('ir.translation')._get_source(cr, uid, self._name+','+f, 'field', lang)
1326                     if res_trans:
1327                         res[lang][f] = res_trans
1328                     else:
1329                         res[lang][f] = self._columns[f].string
1330         for table in self._inherits:
1331             cols = intersect(self._inherit_fields.keys(), fields)
1332             res2 = self.pool.get(table).read_string(cr, uid, id, langs, cols, context)
1333         for lang in res2:
1334             if lang in res:
1335                 res[lang]['code'] = lang
1336             for f in res2[lang]:
1337                 res[lang][f] = res2[lang][f]
1338         return res
1339
1340     def write_string(self, cr, uid, id, langs, vals, context=None):
1341         self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'write')
1342         for lang in langs:
1343             for field in vals:
1344                 if field in self._columns:
1345                     self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field])
1346         for table in self._inherits:
1347             cols = intersect(self._inherit_fields.keys(), vals)
1348             if cols:
1349                 self.pool.get(table).write_string(cr, uid, id, langs, vals, context)
1350         return True
1351
1352     def _check_removed_columns(self, cr, log=False):
1353         raise NotImplementedError()
1354
1355 class orm_memory(orm_template):
1356     _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']
1357     _inherit_fields = {}
1358     _max_count = 200
1359     _max_hours = 1
1360     _check_time = 20
1361
1362     def __init__(self, cr):
1363         super(orm_memory, self).__init__(cr)
1364         self.datas = {}
1365         self.next_id = 0
1366         self.check_id = 0
1367         cr.execute('delete from wkf_instance where res_type=%s', (self._name,))
1368
1369     def vaccum(self, cr, uid):
1370         self.check_id += 1
1371         if self.check_id % self._check_time:
1372             return True
1373         tounlink = []
1374         max = time.time() - self._max_hours * 60 * 60
1375         for id in self.datas:
1376             if self.datas[id]['internal.date_access'] < max:
1377                 tounlink.append(id)
1378         self.unlink(cr, uid, tounlink)
1379         if len(self.datas)>self._max_count:
1380             sorted = map(lambda x: (x[1]['internal.date_access'], x[0]), self.datas.items())
1381             sorted.sort()
1382             ids = map(lambda x: x[1], sorted[:len(self.datas)-self._max_count])
1383             self.unlink(cr, uid, ids)
1384         return True
1385
1386     def read(self, cr, user, ids, fields_to_read=None, context=None, load='_classic_read'):
1387         if not context:
1388             context = {}
1389         if not fields_to_read:
1390             fields_to_read = self._columns.keys()
1391         result = []
1392         if self.datas:
1393             if isinstance(ids, (int, long)):
1394                 ids = [ids]
1395             for id in ids:
1396                 r = {'id': id}
1397                 for f in fields_to_read:
1398                     if id in self.datas:
1399                         r[f] = self.datas[id].get(f, False)
1400                         if r[f] and isinstance(self._columns[f], fields.binary) and context.get('bin_size', False):
1401                             r[f] = len(r[f])
1402                 result.append(r)
1403                 if id in self.datas:
1404                     self.datas[id]['internal.date_access'] = time.time()
1405             fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields_to_read)
1406             for f in fields_post:
1407                 res2 = self._columns[f].get_memory(cr, self, ids, f, user, context=context, values=result)
1408                 for record in result:
1409                     record[f] = res2[record['id']]
1410             if isinstance(ids, (int, long)):
1411                 return result[0]
1412         return result
1413
1414     def write(self, cr, user, ids, vals, context=None):
1415         vals2 = {}
1416         upd_todo = []
1417         for field in vals:
1418             if self._columns[field]._classic_write:
1419                 vals2[field] = vals[field]
1420             else:
1421                 upd_todo.append(field)
1422         for id_new in ids:
1423             self.datas[id_new].update(vals2)
1424             self.datas[id_new]['internal.date_access'] = time.time()
1425             for field in upd_todo:
1426                 self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context)
1427         self._validate(cr, user, [id_new], context)
1428         wf_service = netsvc.LocalService("workflow")
1429         wf_service.trg_write(user, self._name, id_new, cr)
1430         self.vaccum(cr, user)
1431         return id_new
1432
1433     def create(self, cr, user, vals, context=None):
1434         self.next_id += 1
1435         id_new = self.next_id
1436         default = []
1437         for f in self._columns.keys():
1438             if not f in vals:
1439                 default.append(f)
1440         if len(default):
1441             vals.update(self.default_get(cr, user, default, context))
1442         vals2 = {}
1443         upd_todo = []
1444         for field in vals:
1445             if self._columns[field]._classic_write:
1446                 vals2[field] = vals[field]
1447             else:
1448                 upd_todo.append(field)
1449         self.datas[id_new] = vals2
1450         self.datas[id_new]['internal.date_access'] = time.time()
1451
1452         for field in upd_todo:
1453             self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context)
1454         self._validate(cr, user, [id_new], context)
1455         wf_service = netsvc.LocalService("workflow")
1456         wf_service.trg_create(user, self._name, id_new, cr)
1457         self.vaccum(cr, user)
1458         return id_new
1459
1460     def default_get(self, cr, uid, fields_list, context=None):
1461         if not context:
1462             context = {}
1463         value = {}
1464         # get the default values for the inherited fields
1465         for f in fields_list:
1466             if f in self._defaults:
1467                 value[f] = self._defaults[f](self, cr, uid, context)
1468             fld_def = ((f in self._columns) and self._columns[f]) \
1469                     or ((f in self._inherit_fields) and self._inherit_fields[f][2]) \
1470                     or False
1471
1472         # get the default values set by the user and override the default
1473         # values defined in the object
1474         ir_values_obj = self.pool.get('ir.values')
1475         res = ir_values_obj.get(cr, uid, 'default', False, [self._name])
1476         for id, field, field_value in res:
1477             if field in fields_list:
1478                 fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2]
1479                 if fld_def._type in ('many2one', 'one2one'):
1480                     obj = self.pool.get(fld_def._obj)
1481                     if not obj.search(cr, uid, [('id', '=', field_value)]):
1482                         continue
1483                 if fld_def._type in ('many2many'):
1484                     obj = self.pool.get(fld_def._obj)
1485                     field_value2 = []
1486                     for i in range(len(field_value)):
1487                         if not obj.search(cr, uid, [('id', '=',
1488                             field_value[i])]):
1489                             continue
1490                         field_value2.append(field_value[i])
1491                     field_value = field_value2
1492                 if fld_def._type in ('one2many'):
1493                     obj = self.pool.get(fld_def._obj)
1494                     field_value2 = []
1495                     for i in range(len(field_value)):
1496                         field_value2.append({})
1497                         for field2 in field_value[i]:
1498                             if obj._columns[field2]._type in ('many2one', 'one2one'):
1499                                 obj2 = self.pool.get(obj._columns[field2]._obj)
1500                                 if not obj2.search(cr, uid,
1501                                         [('id', '=', field_value[i][field2])]):
1502                                     continue
1503                             # TODO add test for many2many and one2many
1504                             field_value2[i][field2] = field_value[i][field2]
1505                     field_value = field_value2
1506                 value[field] = field_value
1507
1508         # get the default values from the context
1509         for key in context or {}:
1510             if key.startswith('default_'):
1511                 value[key[8:]] = context[key]
1512         return value
1513
1514     def search(self, cr, user, args, offset=0, limit=None, order=None,
1515             context=None, count=False):
1516         return self.datas.keys()
1517
1518     def unlink(self, cr, uid, ids, context=None):
1519         for id in ids:
1520             if id in self.datas:
1521                 del self.datas[id]
1522         if len(ids):
1523             cr.execute('delete from wkf_instance where res_type=%s and res_id in ('+','.join(map(str, ids))+')', (self._name, ))
1524         return True
1525
1526     def perm_read(self, cr, user, ids, context=None, details=True):
1527         result = []
1528         for id in ids:
1529             result.append({
1530                 'create_uid': (user, 'Root'),
1531                 'create_date': time.strftime('%Y-%m-%d %H:%M:%S'),
1532                 'write_uid': False,
1533                 'write_date': False,
1534                 'id': id
1535             })
1536         return result
1537
1538     def _check_removed_columns(self, cr, log=False):
1539         # nothing to check in memory...
1540         pass
1541     
1542     def exists(self, cr, uid, id, context=None):
1543         return id in self.datas
1544
1545 class orm(orm_template):
1546     _sql_constraints = []
1547     _table = None
1548     _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']
1549
1550     def _parent_store_compute(self, cr):
1551         logger = netsvc.Logger()
1552         logger.notifyChannel('orm', netsvc.LOG_INFO, 'Computing parent left and right for table %s...' % (self._table, ))
1553         def browse_rec(root, pos=0):
1554 # TODO: set order
1555             where = self._parent_name+'='+str(root)
1556             if not root:
1557                 where = self._parent_name+' IS NULL'
1558             if self._parent_order:
1559                 where += ' order by '+self._parent_order
1560             cr.execute('SELECT id FROM '+self._table+' WHERE '+where)
1561             pos2 = pos + 1
1562             childs = cr.fetchall()
1563             for id in childs:
1564                 pos2 = browse_rec(id[0], pos2)
1565             cr.execute('update '+self._table+' set parent_left=%s, parent_right=%s where id=%s', (pos,pos2,root))
1566             return pos2+1
1567         query = 'SELECT id FROM '+self._table+' WHERE '+self._parent_name+' IS NULL'
1568         if self._parent_order:
1569             query += ' order by '+self._parent_order
1570         pos = 0
1571         cr.execute(query)
1572         for (root,) in cr.fetchall():
1573             pos = browse_rec(root, pos)
1574         return True
1575
1576     def _update_store(self, cr, f, k):
1577         logger = netsvc.Logger()
1578         logger.notifyChannel('orm', netsvc.LOG_INFO, "storing computed values of fields.function '%s'" % (k,))
1579         ss = self._columns[k]._symbol_set
1580         update_query = 'UPDATE "%s" SET "%s"=%s WHERE id=%%s' % (self._table, k, ss[0])
1581         cr.execute('select id from '+self._table)
1582         ids_lst = map(lambda x: x[0], cr.fetchall())
1583         while ids_lst:
1584             iids = ids_lst[:40]
1585             ids_lst = ids_lst[40:]
1586             res = f.get(cr, self, iids, k, 1, {})
1587             for key,val in res.items():
1588                 if f._multi:
1589                     val = val[k]
1590                 # if val is a many2one, just write the ID
1591                 if type(val)==tuple:
1592                     val = val[0]
1593                 if (val<>False) or (type(val)<>bool):
1594                     cr.execute(update_query, (ss[1](val), key))
1595
1596     def _check_removed_columns(self, cr, log=False):
1597         logger = netsvc.Logger()
1598         # iterate on the database columns to drop the NOT NULL constraints
1599         # of fields which were required but have been removed (or will be added by another module)
1600         columns = [c for c in self._columns if not (isinstance(self._columns[c], fields.function) and not self._columns[c].store)]
1601         columns += ('id', 'write_uid', 'write_date', 'create_uid', 'create_date') # openerp access columns
1602         cr.execute("SELECT a.attname, a.attnotnull"
1603                    "  FROM pg_class c, pg_attribute a"
1604                    " WHERE c.relname=%%s"
1605                    "   AND c.oid=a.attrelid"
1606                    "   AND a.attisdropped=%%s"
1607                    "   AND pg_catalog.format_type(a.atttypid, a.atttypmod) NOT IN ('cid', 'tid', 'oid', 'xid')"
1608                    "   AND a.attname NOT IN (%s)" % ",".join(['%s']*len(columns)),
1609                        [self._table, False] + columns)
1610         for column in cr.dictfetchall():
1611             if log:
1612                 logger.notifyChannel("orm", netsvc.LOG_DEBUG, "column %s is in the table %s but not in the corresponding object %s" % (column['attname'], self._table, self._name))
1613             if column['attnotnull']:
1614                 cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, column['attname']))
1615
1616     def _auto_init(self, cr, context={}):
1617         store_compute =  False
1618         logger = netsvc.Logger()
1619         create = False
1620         todo_end = []
1621         self._field_create(cr, context=context)
1622         if not hasattr(self, "_auto") or self._auto:
1623             cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table)
1624             if not cr.rowcount:
1625                 cr.execute("CREATE TABLE \"%s\" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITH OIDS" % self._table)
1626                 create = True
1627             cr.commit()
1628             if self._parent_store:
1629                 cr.execute("""SELECT c.relname
1630                     FROM pg_class c, pg_attribute a
1631                     WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid
1632                     """, (self._table, 'parent_left'))
1633                 if not cr.rowcount:
1634                     if 'parent_left' not in self._columns:
1635                         logger.notifyChannel('orm', netsvc.LOG_ERROR, 'create a column parent_left on object %s: fields.integer(\'Left Parent\', select=1)' % (self._table, ))
1636                     if 'parent_right' not in self._columns:
1637                         logger.notifyChannel('orm', netsvc.LOG_ERROR, 'create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)' % (self._table, ))
1638                     if self._columns[self._parent_name].ondelete<>'cascade':
1639                         logger.notifyChannel('orm', netsvc.LOG_ERROR, "the columns %s on object must be set as ondelete='cascasde'" % (self._name, self._parent_name))
1640                     cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_left" INTEGER' % (self._table,))
1641                     cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_right" INTEGER' % (self._table,))
1642                     cr.commit()
1643                     store_compute = True
1644
1645             if self._log_access:
1646                 logs = {
1647                     'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL',
1648                     'create_date': 'TIMESTAMP',
1649                     'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL',
1650                     'write_date': 'TIMESTAMP'
1651                 }
1652                 for k in logs:
1653                     cr.execute("""
1654                         SELECT c.relname
1655                           FROM pg_class c, pg_attribute a
1656                          WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid
1657                         """, (self._table, k))
1658                     if not cr.rowcount:
1659                         cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, logs[k]))
1660                         cr.commit()
1661
1662             self._check_removed_columns(cr, log=False)
1663
1664             # iterate on the "object columns"
1665             todo_update_store = []
1666             for k in self._columns:
1667                 if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'):
1668                     continue
1669                     #raise _('Can not define a column %s. Reserved keyword !') % (k,)
1670                 f = self._columns[k]
1671
1672                 if isinstance(f, fields.one2many):
1673                     cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,))
1674                     if cr.fetchone():
1675                         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))
1676                         res = cr.fetchone()[0]
1677                         if not res:
1678                             cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY (%s) REFERENCES "%s" ON DELETE SET NULL' % (self._obj, f._fields_id, f._table))
1679                 elif isinstance(f, fields.many2many):
1680                     cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (f._rel,))
1681                     if not cr.dictfetchall():
1682                         #FIXME: Remove this try/except
1683                         try:
1684                             ref = self.pool.get(f._obj)._table
1685                         except AttributeError:
1686                             ref = f._obj.replace('.', '_')
1687                         cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL REFERENCES "%s" ON DELETE CASCADE, "%s" INTEGER NOT NULL REFERENCES "%s" ON DELETE CASCADE) WITH OIDS' % (f._rel, f._id1, self._table, f._id2, ref))
1688                         cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id1, f._rel, f._id1))
1689                         cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id2, f._rel, f._id2))
1690                         cr.commit()
1691                 else:
1692                     cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \
1693                                "FROM pg_class c,pg_attribute a,pg_type t " \
1694                                "WHERE c.relname=%s " \
1695                                "AND a.attname=%s " \
1696                                "AND c.oid=a.attrelid " \
1697                                "AND a.atttypid=t.oid", (self._table, k))
1698                     res = cr.dictfetchall()
1699                     if not res:
1700                         if not isinstance(f, fields.function) or f.store:
1701
1702                             # add the missing field
1703                             cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1]))
1704
1705                             # initialize it
1706                             if not create and k in self._defaults:
1707                                 default = self._defaults[k](self, cr, 1, {})
1708                                 ss = self._columns[k]._symbol_set
1709                                 query = 'UPDATE "%s" SET "%s"=%s' % (self._table, k, ss[0])
1710                                 cr.execute(query, (ss[1](default),))
1711                                 cr.commit()
1712                                 logger.notifyChannel('orm', netsvc.LOG_DEBUG, 'setting default value of new column %s of table %s'% (k, self._table))
1713                             elif not create:
1714                                 logger.notifyChannel('orm', netsvc.LOG_DEBUG, 'creating new column %s of table %s'% (k, self._table))
1715
1716                             if isinstance(f, fields.function):
1717                                 order = 10
1718                                 if f.store is not True:
1719                                     order = f.store[f.store.keys()[0]][2]
1720                                 todo_update_store.append((order, f,k))
1721
1722                             # and add constraints if needed
1723                             if isinstance(f, fields.many2one):
1724                                 #FIXME: Remove this try/except
1725                                 try:
1726                                     ref = self.pool.get(f._obj)._table
1727                                 except AttributeError:
1728                                     ref = f._obj.replace('.', '_')
1729                                 # ir_actions is inherited so foreign key doesn't work on it
1730                                 if ref != 'ir_actions':
1731                                     cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY ("%s") REFERENCES "%s" ON DELETE %s' % (self._table, k, ref, f.ondelete))
1732                             if f.select:
1733                                 cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k))
1734                             if f.required:
1735                                 try:
1736                                     cr.commit()
1737                                     cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL' % (self._table, k))
1738                                 except Exception, e:
1739                                     logger.notifyChannel('orm', netsvc.LOG_WARNING, 'WARNING: unable to set column %s of table %s not null !\nTry to re-run: openerp-server.py --update=module\nIf it doesn\'t work, update records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k))
1740                             cr.commit()
1741                     elif len(res)==1:
1742                         f_pg_def = res[0]
1743                         f_pg_type = f_pg_def['typname']
1744                         f_pg_size = f_pg_def['size']
1745                         f_pg_notnull = f_pg_def['attnotnull']
1746                         if isinstance(f, fields.function) and not f.store:
1747                             logger.notifyChannel('orm', netsvc.LOG_INFO, 'column %s (%s) in table %s removed: converted to a function !\n' % (k, f.string, self._table))
1748                             cr.execute('ALTER TABLE %s DROP COLUMN %s'% (self._table, k))
1749                             cr.commit()
1750                             f_obj_type = None
1751                         else:
1752                             f_obj_type = get_pg_type(f) and get_pg_type(f)[0]
1753
1754                         if f_obj_type:
1755                             ok = False
1756                             casts = [
1757                                 ('text', 'char', 'VARCHAR(%d)' % (f.size or 0,), '::VARCHAR(%d)'%(f.size or 0,)),
1758                                 ('varchar', 'text', 'TEXT', ''),
1759                                 ('int4', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
1760                                 ('date', 'datetime', 'TIMESTAMP', '::TIMESTAMP'),
1761                             ]
1762                             # !!! Avoid reduction of varchar field !!!
1763                             if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size < f.size:
1764                             # if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size != f.size:
1765                                 logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed size" % (k, self._table))
1766                                 cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
1767                                 cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" VARCHAR(%d)' % (self._table, k, f.size))
1768                                 cr.execute('UPDATE "%s" SET "%s"=temp_change_size::VARCHAR(%d)' % (self._table, k, f.size))
1769                                 cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size' % (self._table,))
1770                                 cr.commit()
1771                             for c in casts:
1772                                 if (f_pg_type==c[0]) and (f._type==c[1]):
1773                                     logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed type to %s." % (k, self._table, c[1]))
1774                                     ok = True
1775                                     cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
1776                                     cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2]))
1777                                     cr.execute(('UPDATE "%s" SET "%s"=temp_change_size'+c[3]) % (self._table, k))
1778                                     cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
1779                                     cr.commit()
1780
1781                             if f_pg_type != f_obj_type:
1782                                 if not ok:
1783                                     logger.notifyChannel('orm', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed type (DB = %s, def = %s) but unable to migrate this change !" % (k, self._table, f_pg_type, f._type))
1784
1785                             # if the field is required and hasn't got a NOT NULL constraint
1786                             if f.required and f_pg_notnull == 0:
1787                                 # set the field to the default value if any
1788                                 if k in self._defaults:
1789                                     default = self._defaults[k](self, cr, 1, {})
1790                                     if (default is not None):
1791                                         ss = self._columns[k]._symbol_set
1792                                         query = 'UPDATE "%s" SET "%s"=%s WHERE %s is NULL' % (self._table, k, ss[0], k)
1793                                         cr.execute(query, (ss[1](default),))
1794                                 # add the NOT NULL constraint
1795                                 cr.commit()
1796                                 try:
1797                                     cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL' % (self._table, k))
1798                                     cr.commit()
1799                                 except Exception, e:
1800                                     logger.notifyChannel('orm', netsvc.LOG_WARNING, 'unable to set a NOT NULL constraint on column %s of the %s table !\nIf you want to have it, you should update the records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k))
1801                                 cr.commit()
1802                             elif not f.required and f_pg_notnull == 1:
1803                                 cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, k))
1804                                 cr.commit()
1805                             indexname = '%s_%s_index' % (self._table, k)
1806                             cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = %s and tablename = %s", (indexname, self._table))
1807                             res = cr.dictfetchall()
1808                             if not res and f.select:
1809                                 cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k))
1810                                 cr.commit()
1811                             if res and not f.select:
1812                                 cr.execute('DROP INDEX "%s_%s_index"' % (self._table, k))
1813                                 cr.commit()
1814                             if isinstance(f, fields.many2one):
1815                                 ref = self.pool.get(f._obj)._table
1816                                 if ref != 'ir_actions':
1817                                     cr.execute('SELECT confdeltype, conname FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, '
1818                                                 'pg_attribute as att1, pg_attribute as att2 '
1819                                             'WHERE con.conrelid = cl1.oid '
1820                                                 'AND cl1.relname = %s '
1821                                                 'AND con.confrelid = cl2.oid '
1822                                                 'AND cl2.relname = %s '
1823                                                 'AND array_lower(con.conkey, 1) = 1 '
1824                                                 'AND con.conkey[1] = att1.attnum '
1825                                                 'AND att1.attrelid = cl1.oid '
1826                                                 'AND att1.attname = %s '
1827                                                 'AND array_lower(con.confkey, 1) = 1 '
1828                                                 'AND con.confkey[1] = att2.attnum '
1829                                                 'AND att2.attrelid = cl2.oid '
1830                                                 'AND att2.attname = %s '
1831                                                 "AND con.contype = 'f'", (self._table, ref, k, 'id'))
1832                                     res = cr.dictfetchall()
1833                                     if res:
1834                                         confdeltype = {
1835                                             'RESTRICT': 'r',
1836                                             'NO ACTION': 'a',
1837                                             'CASCADE': 'c',
1838                                             'SET NULL': 'n',
1839                                             'SET DEFAULT': 'd',
1840                                         }
1841                                         if res[0]['confdeltype'] != confdeltype.get(f.ondelete.upper(), 'a'):
1842                                             cr.execute('ALTER TABLE "' + self._table + '" DROP CONSTRAINT "' + res[0]['conname'] + '"')
1843                                             cr.execute('ALTER TABLE "' + self._table + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete)
1844                                             cr.commit()
1845                     else:
1846                         logger.notifyChannel('orm', netsvc.LOG_ERROR, "Programming error !")
1847             for order,f,k in todo_update_store:
1848                 todo_end.append((order, self._update_store, (f, k)))
1849
1850         else:
1851             cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (self._table,))
1852             create = not bool(cr.fetchone())
1853
1854         for (key, con, _) in self._sql_constraints:
1855             conname = '%s_%s' % (self._table, key)
1856             cr.execute("SELECT conname FROM pg_constraint where conname=%s", (conname,))
1857             if not cr.dictfetchall():
1858                 try:
1859                     cr.execute('alter table "%s" add constraint "%s_%s" %s' % (self._table, self._table, key, con,))
1860                     cr.commit()
1861                 except:
1862                     logger.notifyChannel('orm', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table, self._table, key, con,))
1863
1864         if create:
1865             if hasattr(self, "_sql"):
1866                 for line in self._sql.split(';'):
1867                     line2 = line.replace('\n', '').strip()
1868                     if line2:
1869                         cr.execute(line2)
1870                         cr.commit()
1871         if store_compute:
1872             self._parent_store_compute(cr)
1873         return todo_end
1874
1875     def __init__(self, cr):
1876         super(orm, self).__init__(cr)
1877
1878         if not hasattr(self, '_log_access'):
1879             # if not access is not specify, it is the same value as _auto
1880             self._log_access = not hasattr(self, "_auto") or self._auto
1881
1882         self._columns = self._columns.copy()
1883         for store_field in self._columns:
1884             f = self._columns[store_field]
1885             if not isinstance(f, fields.function):
1886                 continue
1887             if not f.store:
1888                 continue
1889             if self._columns[store_field].store is True:
1890                 sm = {self._name:(lambda self,cr, uid, ids, c={}: ids, None, 10)}
1891             else:
1892                 sm = self._columns[store_field].store
1893             for object, aa in sm.items():
1894                 if len(aa)==3:
1895                     (fnct,fields2,order)=aa
1896                 else:
1897                     raise except_orm('Error',
1898                         ('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority)}.' % (store_field, self._name)))
1899                 self.pool._store_function.setdefault(object, [])
1900                 ok = True
1901                 for x,y,z,e,f in self.pool._store_function[object]:
1902                     if (x==self._name) and (y==store_field) and (e==fields2):
1903                         ok = False
1904                 if ok:
1905                     self.pool._store_function[object].append( (self._name, store_field, fnct, fields2, order))
1906                     self.pool._store_function[object].sort(lambda x,y: cmp(x[4],y[4]))
1907
1908         for (key, _, msg) in self._sql_constraints:
1909             self.pool._sql_error[self._table+'_'+key] = msg
1910
1911         # Load manual fields
1912
1913         cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state', 'ir.model.fields'))
1914         if cr.fetchone():
1915             cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name, 'manual'))
1916             for field in cr.dictfetchall():
1917                 if field['name'] in self._columns:
1918                     continue
1919                 attrs = {
1920                     'string': field['field_description'],
1921                     'required': bool(field['required']),
1922                     'readonly': bool(field['readonly']),
1923                     'domain': field['domain'] or None,
1924                     'size': field['size'],
1925                     'ondelete': field['on_delete'],
1926                     'translate': (field['translate']),
1927                     #'select': int(field['select_level'])
1928                 }
1929
1930                 if field['ttype'] == 'selection':
1931                     self._columns[field['name']] = getattr(fields, field['ttype'])(eval(field['selection']), **attrs)
1932                 elif field['ttype'] == 'reference':
1933                     self._columns[field['name']] = getattr(fields, field['ttype'])(selection=eval(field['selection']), **attrs)
1934                 elif field['ttype'] == 'many2one':
1935                     self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], **attrs)
1936                 elif field['ttype'] == 'one2many':
1937                     self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], field['relation_field'], **attrs)
1938                 elif field['ttype'] == 'many2many':
1939                     import random
1940                     _rel1 = field['relation'].replace('.', '_')
1941                     _rel2 = field['model'].replace('.', '_')
1942                     _rel_name = 'x_%s_%s_%s_rel' %(_rel1, _rel2, field['name'])
1943                     self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], _rel_name, 'id1', 'id2', **attrs)
1944                 else:
1945                     self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs)
1946
1947         self._inherits_reload()
1948         if not self._sequence:
1949             self._sequence = self._table+'_id_seq'
1950         for k in self._defaults:
1951             assert (k in self._columns) or (k in self._inherit_fields), 'Default function defined in %s but field %s does not exist !' % (self._name, k,)
1952         for f in self._columns:
1953             self._columns[f].restart()
1954
1955     def default_get(self, cr, uid, fields_list, context=None):
1956         if not context:
1957             context = {}
1958         value = {}
1959         # get the default values for the inherited fields
1960         for t in self._inherits.keys():
1961             value.update(self.pool.get(t).default_get(cr, uid, fields_list,
1962                 context))
1963
1964         # get the default values defined in the object
1965         for f in fields_list:
1966             if f in self._defaults:
1967                 value[f] = self._defaults[f](self, cr, uid, context)
1968             fld_def = ((f in self._columns) and self._columns[f]) \
1969                     or ((f in self._inherit_fields) and self._inherit_fields[f][2]) \
1970                     or False
1971             if isinstance(fld_def, fields.property):
1972                 property_obj = self.pool.get('ir.property')
1973                 definition_id = fld_def._field_get(cr, uid, self._name, f)
1974                 nid = property_obj.search(cr, uid, [('fields_id', '=',
1975                     definition_id), ('res_id', '=', False)])
1976                 if nid:
1977                     prop_value = property_obj.browse(cr, uid, nid[0],
1978                             context=context).value
1979                     value[f] = (prop_value and int(prop_value.split(',')[1])) \
1980                             or False
1981
1982         # get the default values set by the user and override the default
1983         # values defined in the object
1984         ir_values_obj = self.pool.get('ir.values')
1985         res = ir_values_obj.get(cr, uid, 'default', False, [self._name])
1986         for id, field, field_value in res:
1987             if field in fields_list:
1988                 fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2]
1989                 if fld_def._type in ('many2one', 'one2one'):
1990                     obj = self.pool.get(fld_def._obj)
1991                     if not obj.search(cr, uid, [('id', '=', field_value)]):
1992                         continue
1993                 if fld_def._type in ('many2many'):
1994                     obj = self.pool.get(fld_def._obj)
1995                     field_value2 = []
1996                     for i in range(len(field_value)):
1997                         if not obj.search(cr, uid, [('id', '=',
1998                             field_value[i])]):
1999                             continue
2000                         field_value2.append(field_value[i])
2001                     field_value = field_value2
2002                 if fld_def._type in ('one2many'):
2003                     obj = self.pool.get(fld_def._obj)
2004                     field_value2 = []
2005                     for i in range(len(field_value)):
2006                         field_value2.append({})
2007                         for field2 in field_value[i]:
2008                             if obj._columns[field2]._type in ('many2one', 'one2one'):
2009                                 obj2 = self.pool.get(obj._columns[field2]._obj)
2010                                 if not obj2.search(cr, uid,
2011                                         [('id', '=', field_value[i][field2])]):
2012                                     continue
2013                             # TODO add test for many2many and one2many
2014                             field_value2[i][field2] = field_value[i][field2]
2015                     field_value = field_value2
2016                 value[field] = field_value
2017         for key in context or {}:
2018             if key.startswith('default_'):
2019                 value[key[8:]] = context[key]
2020         return value
2021
2022     #
2023     # Update objects that uses this one to update their _inherits fields
2024     #
2025     def _inherits_reload_src(self):
2026         for obj in self.pool.obj_pool.values():
2027             if self._name in obj._inherits:
2028                 obj._inherits_reload()
2029
2030     def _inherits_reload(self):
2031         res = {}
2032         for table in self._inherits:
2033             res.update(self.pool.get(table)._inherit_fields)
2034             for col in self.pool.get(table)._columns.keys():
2035                 res[col] = (table, self._inherits[table], self.pool.get(table)._columns[col])
2036             for col in self.pool.get(table)._inherit_fields.keys():
2037                 res[col] = (table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2])
2038         self._inherit_fields = res
2039         self._inherits_reload_src()
2040
2041     def fields_get(self, cr, user, fields=None, context=None):
2042         read_access = self.pool.get('ir.model.access').check(cr, user, self._name, 'write', raise_exception=False)
2043         return super(orm, self).fields_get(cr, user, fields, context, read_access)
2044
2045     def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
2046         if not context:
2047             context = {}
2048         self.pool.get('ir.model.access').check(cr, user, self._name, 'read')
2049         if not fields:
2050             fields = self._columns.keys() + self._inherit_fields.keys()
2051         select = ids
2052         if isinstance(ids, (int, long)):
2053             select = [ids]
2054         result = self._read_flat(cr, user, select, fields, context, load)
2055         for r in result:
2056             for key, v in r.items():
2057                 if v == None:
2058                     r[key] = False
2059         if isinstance(ids, (int, long)):
2060             return result and result[0] or False
2061         return result
2062
2063     def _read_flat(self, cr, user, ids, fields_to_read, context=None, load='_classic_read'):
2064         if not context:
2065             context = {}
2066         if not ids:
2067             return []
2068
2069         if fields_to_read == None:
2070             fields_to_read = self._columns.keys()
2071
2072         # construct a clause for the rules :
2073         d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
2074
2075         # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
2076         fields_pre = [f for f in fields_to_read if
2077                            f == self.CONCURRENCY_CHECK_FIELD
2078                         or (f in self._columns and getattr(self._columns[f], '_classic_write'))
2079                      ] + self._inherits.values()
2080
2081         res = []
2082         if len(fields_pre):
2083             def convert_field(f):
2084                 if f in ('create_date', 'write_date'):
2085                     return "date_trunc('second', %s) as %s" % (f, f)
2086                 if f == self.CONCURRENCY_CHECK_FIELD:
2087                     if self._log_access:
2088                         return "COALESCE(write_date, create_date, now())::timestamp AS %s" % (f,)
2089                     return "now()::timestamp AS %s" % (f,)
2090                 if isinstance(self._columns[f], fields.binary) and context.get('bin_size', False):
2091                     return 'length("%s") as "%s"' % (f, f)
2092                 return '"%s"' % (f,)
2093             fields_pre2 = map(convert_field, fields_pre)
2094             for i in range(0, len(ids), cr.IN_MAX):
2095                 sub_ids = ids[i:i+cr.IN_MAX]
2096                 if d1:
2097                     cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) AND %s ORDER BY %s' % \
2098                             (','.join(fields_pre2 + ['id']), self._table,
2099                                 ','.join([str(x) for x in sub_ids]), d1,
2100                                 self._order), d2)
2101                     if not cr.rowcount == len({}.fromkeys(sub_ids)):
2102                         raise except_orm(_('AccessError'),
2103                                 _('You try to bypass an access rule (Document type: %s).') % self._description)
2104                 else:
2105                     cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) ORDER BY %s' % \
2106                             (','.join(fields_pre2 + ['id']), self._table,
2107                                 ','.join([str(x) for x in sub_ids]),
2108                                 self._order))
2109                 res.extend(cr.dictfetchall())
2110         else:
2111             res = map(lambda x: {'id': x}, ids)
2112
2113         for f in fields_pre:
2114             if f == self.CONCURRENCY_CHECK_FIELD:
2115                 continue
2116             if self._columns[f].translate:
2117                 ids = map(lambda x: x['id'], res)
2118                 res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
2119                 for r in res:
2120                     r[f] = res_trans.get(r['id'], False) or r[f]
2121
2122         for table in self._inherits:
2123             col = self._inherits[table]
2124             cols = intersect(self._inherit_fields.keys(), fields_to_read)
2125             if not cols:
2126                 continue
2127             res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
2128
2129             res3 = {}
2130             for r in res2:
2131                 res3[r['id']] = r
2132                 del r['id']
2133
2134             for record in res:
2135                 record.update(res3[record[col]])
2136                 if col not in fields_to_read:
2137                     del record[col]
2138
2139         # all fields which need to be post-processed by a simple function (symbol_get)
2140         fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields_to_read)
2141         if fields_post:
2142             # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
2143             # to get the _symbol_get in each occurence
2144             for r in res:
2145                 for f in fields_post:
2146                     r[f] = self._columns[f]._symbol_get(r[f])
2147         ids = map(lambda x: x['id'], res)
2148
2149         # all non inherited fields for which the attribute whose name is in load is False
2150         fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields_to_read)
2151
2152         # Compute POST fields
2153         todo = {}
2154         for f in fields_post:
2155             todo.setdefault(self._columns[f]._multi, [])
2156             todo[self._columns[f]._multi].append(f)
2157         for key,val in todo.items():
2158             if key:
2159                 res2 = self._columns[val[0]].get(cr, self, ids, val, user, context=context, values=res)
2160                 for pos in val:
2161                     for record in res:
2162                         record[pos] = res2[record['id']][pos]
2163             else:
2164                 for f in val:
2165                     res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
2166                     for record in res:
2167                         record[f] = res2[record['id']]
2168
2169 #for f in fields_post:
2170 #    # get the value of that field for all records/ids
2171 #    res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
2172 #    for record in res:
2173 #        record[f] = res2[record['id']]
2174
2175         readonly = None
2176         for vals in res:
2177             for field in vals.copy():
2178                 fobj = None
2179                 if field in self._columns:
2180                     fobj = self._columns[field]
2181
2182                 if not fobj:
2183                     continue
2184                 groups = fobj.read
2185                 if groups:
2186                     edit = False
2187                     for group in groups:
2188                         module = group.split(".")[0]
2189                         grp = group.split(".")[1]
2190                         cr.execute("select count(*) from res_groups_users_rel where gid in (select res_id from ir_model_data where name='%s' and module='%s' and model='%s') and uid=%s" % \
2191                                    (grp, module, 'res.groups', user))
2192                         readonly = cr.fetchall()
2193                         if readonly[0][0] >= 1:
2194                             edit = True
2195                             break
2196                         elif readonly[0][0] == 0:
2197                             edit = False
2198                         else:
2199                             edit = False
2200
2201                     if not edit:
2202                         if type(vals[field]) == type([]):
2203                             vals[field] = []
2204                         elif type(vals[field]) == type(0.0):
2205                             vals[field] = 0
2206                         elif type(vals[field]) == type(''):
2207                             vals[field] = '=No Permission='
2208                         else:
2209                             vals[field] = False
2210         return res
2211
2212     def perm_read(self, cr, user, ids, context=None, details=True):
2213         if not context:
2214             context = {}
2215         if not ids:
2216             return []
2217         fields = ''
2218         if self._log_access:
2219             fields = ', u.create_uid, u.create_date, u.write_uid, u.write_date'
2220         if isinstance(ids, (int, long)):
2221             ids_str = str(ids)
2222         else:
2223             ids_str = string.join(map(lambda x: str(x), ids), ',')
2224         cr.execute('select u.id'+fields+' from "'+self._table+'" u where u.id in ('+ids_str+')')
2225         res = cr.dictfetchall()
2226         for r in res:
2227             for key in r:
2228                 r[key] = r[key] or False
2229                 if key in ('write_uid', 'create_uid', 'uid') and details:
2230                     if r[key]:
2231                         r[key] = self.pool.get('res.users').name_get(cr, user, [r[key]])[0]
2232         if isinstance(ids, (int, long)):
2233             return res[ids]
2234         return res
2235
2236     def _check_concurrency(self, cr, ids, context):
2237         if not context:
2238             return
2239         if context.get(self.CONCURRENCY_CHECK_FIELD) and self._log_access:
2240             def key(oid):
2241                 return "%s,%s" % (self._name, oid)
2242             santa = "(id = %s AND %s < COALESCE(write_date, create_date, now())::timestamp)"
2243             for i in range(0, len(ids), cr.IN_MAX):
2244                 sub_ids = tools.flatten(((oid, context[self.CONCURRENCY_CHECK_FIELD][key(oid)])
2245                                           for oid in ids[i:i+cr.IN_MAX]
2246                                           if key(oid) in context[self.CONCURRENCY_CHECK_FIELD]))
2247                 if sub_ids:
2248                     cr.execute("SELECT count(1) FROM %s WHERE %s" % (self._table, " OR ".join([santa]*(len(sub_ids)/2))), sub_ids)
2249                     res = cr.fetchone()
2250                     if res and res[0]:
2251                         raise except_orm('ConcurrencyException', _('Records were modified in the meanwhile'))
2252
2253     def unlink(self, cr, uid, ids, context=None):
2254         if not ids:
2255             return True
2256         if isinstance(ids, (int, long)):
2257             ids = [ids]
2258
2259         result_store = self._store_get_values(cr, uid, ids, None, context)
2260
2261         self._check_concurrency(cr, ids, context)
2262
2263         self.pool.get('ir.model.access').check(cr, uid, self._name, 'unlink')
2264
2265         wf_service = netsvc.LocalService("workflow")
2266         for id in ids:
2267             wf_service.trg_delete(uid, self._name, id, cr)
2268
2269         #cr.execute('select * from '+self._table+' where id in ('+str_d+')', ids)
2270         #res = cr.dictfetchall()
2271         #for key in self._inherits:
2272         #   ids2 = [x[self._inherits[key]] for x in res]
2273         #   self.pool.get(key).unlink(cr, uid, ids2)
2274
2275         d1, d2 = self.pool.get('ir.rule').domain_get(cr, uid, self._name)
2276         if d1:
2277             d1 = ' AND '+d1
2278
2279         for i in range(0, len(ids), cr.IN_MAX):
2280             sub_ids = ids[i:i+cr.IN_MAX]
2281             str_d = string.join(('%s',)*len(sub_ids), ',')
2282             if d1:
2283                 cr.execute('SELECT id FROM "'+self._table+'" ' \
2284                         'WHERE id IN ('+str_d+')'+d1, sub_ids+d2)
2285                 if not cr.rowcount == len({}.fromkeys(ids)):
2286                     raise except_orm(_('AccessError'),
2287                             _('You try to bypass an access rule (Document type: %s).') % \
2288                                     self._description)
2289
2290             if d1:
2291                 cr.execute('delete from "'+self._table+'" ' \
2292                         'where id in ('+str_d+')'+d1, sub_ids+d2)
2293             else:
2294                 cr.execute('delete from "'+self._table+'" ' \
2295                         'where id in ('+str_d+')', sub_ids)
2296
2297         for order, object, ids, fields in result_store:
2298             if object<>self._name:
2299                 obj =  self.pool.get(object)
2300                 cr.execute('select id from '+obj._table+' where id in ('+','.join(map(str, ids))+')')
2301                 ids = map(lambda x: x[0], cr.fetchall())
2302                 if ids:
2303                     obj._store_set_values(cr, uid, ids, fields, context)
2304         return True
2305
2306     #
2307     # TODO: Validate
2308     #
2309     def write(self, cr, user, ids, vals, context=None):
2310         readonly = None
2311         for field in vals.copy():
2312             fobj = None
2313             if field in self._columns:
2314                 fobj = self._columns[field]
2315             else:
2316                 fobj = self._inherit_fields[field][2]
2317             if not fobj:
2318                 continue
2319             groups = fobj.write
2320
2321             if groups:
2322                 edit = False
2323                 for group in groups:
2324                     module = group.split(".")[0]
2325                     grp = group.split(".")[1]
2326                     cr.execute("select count(*) from res_groups_users_rel where gid in (select res_id from ir_model_data where name='%s' and module='%s' and model='%s') and uid=%s" % \
2327                                (grp, module, 'res.groups', user))
2328                     readonly = cr.fetchall()
2329                     if readonly[0][0] >= 1:
2330                         edit = True
2331                         break
2332                     elif readonly[0][0] == 0:
2333                         edit = False
2334                     else:
2335                         edit = False
2336
2337                 if not edit:
2338                     vals.pop(field)
2339
2340         if not context:
2341             context = {}
2342         if not ids:
2343             return True
2344         if isinstance(ids, (int, long)):
2345             ids = [ids]
2346
2347         self._check_concurrency(cr, ids, context)
2348
2349         self.pool.get('ir.model.access').check(cr, user, self._name, 'write')
2350
2351         upd0 = []
2352         upd1 = []
2353         upd_todo = []
2354         updend = []
2355         direct = []
2356         totranslate = context.get('lang', False) and (context['lang'] != 'en_US')
2357         for field in vals:
2358             if field in self._columns:
2359                 if self._columns[field]._classic_write and not (hasattr(self._columns[field], '_fnct_inv')):
2360                     if (not totranslate) or not self._columns[field].translate:
2361                         upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0])
2362                         upd1.append(self._columns[field]._symbol_set[1](vals[field]))
2363                     direct.append(field)
2364                 else:
2365                     upd_todo.append(field)
2366             else:
2367                 updend.append(field)
2368             if field in self._columns \
2369                     and hasattr(self._columns[field], 'selection') \
2370                     and vals[field]:
2371                 if self._columns[field]._type == 'reference':
2372                     val = vals[field].split(',')[0]
2373                 else:
2374                     val = vals[field]
2375                 if isinstance(self._columns[field].selection, (tuple, list)):
2376                     if val not in dict(self._columns[field].selection):
2377                         raise except_orm(_('ValidateError'),
2378                         _('The value "%s" for the field "%s" is not in the selection') \
2379                                 % (vals[field], field))
2380                 else:
2381                     if val not in dict(self._columns[field].selection(
2382                         self, cr, user, context=context)):
2383                         raise except_orm(_('ValidateError'),
2384                         _('The value "%s" for the field "%s" is not in the selection') \
2385                                 % (vals[field], field))
2386
2387         if self._log_access:
2388             upd0.append('write_uid=%s')
2389             upd0.append('write_date=now()')
2390             upd1.append(user)
2391
2392         if len(upd0):
2393
2394             d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
2395             if d1:
2396                 d1 = ' and '+d1
2397
2398             for i in range(0, len(ids), cr.IN_MAX):
2399                 sub_ids = ids[i:i+cr.IN_MAX]
2400                 ids_str = string.join(map(str, sub_ids), ',')
2401                 if d1:
2402                     cr.execute('SELECT id FROM "'+self._table+'" ' \
2403                             'WHERE id IN ('+ids_str+')'+d1, d2)
2404                     if not cr.rowcount == len({}.fromkeys(sub_ids)):
2405                         raise except_orm(_('AccessError'),
2406                                 _('You try to bypass an access rule (Document type: %s).') % \
2407                                         self._description)
2408                 else:
2409                     cr.execute('SELECT id FROM "'+self._table+'" WHERE id IN ('+ids_str+')')
2410                     if not cr.rowcount == len({}.fromkeys(sub_ids)):
2411                         raise except_orm(_('AccessError'),
2412                                 _('You try to write on an record that doesn\'t exist ' \
2413                                         '(Document type: %s).') % self._description)
2414                 if d1:
2415                     cr.execute('update "'+self._table+'" set '+string.join(upd0, ',')+' ' \
2416                             'where id in ('+ids_str+')'+d1, upd1+ d2)
2417                 else:
2418                     cr.execute('update "'+self._table+'" set '+string.join(upd0, ',')+' ' \
2419                             'where id in ('+ids_str+')', upd1)
2420             
2421             if totranslate:
2422                 for f in direct:
2423                     if self._columns[f].translate:
2424                         src_trans = self.pool.get(self._name).read(cr,user,ids,[f])
2425                         self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans[0][f])
2426
2427         # call the 'set' method of fields which are not classic_write
2428         upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
2429
2430         # default element in context must be remove when call a one2many or many2many
2431         rel_context = context.copy()
2432         for c in context.items():
2433             if c[0].startswith('default_'):
2434                 del rel_context[c[0]]
2435
2436         for field in upd_todo:
2437             for id in ids:
2438                 self._columns[field].set(cr, self, id, field, vals[field], user, context=rel_context)
2439
2440         for table in self._inherits:
2441             col = self._inherits[table]
2442             nids = []
2443             for i in range(0, len(ids), cr.IN_MAX):
2444                 sub_ids = ids[i:i+cr.IN_MAX]
2445                 ids_str = string.join(map(str, sub_ids), ',')
2446                 cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \
2447                         'where id in ('+ids_str+')', upd1)
2448                 nids.extend([x[0] for x in cr.fetchall()])
2449
2450             v = {}
2451             for val in updend:
2452                 if self._inherit_fields[val][0] == table:
2453                     v[val] = vals[val]
2454             self.pool.get(table).write(cr, user, nids, v, context)
2455
2456         self._validate(cr, user, ids, context)
2457 # TODO: use _order to set dest at the right position and not first node of parent
2458         if self._parent_store and (self._parent_name in vals):
2459             if self.pool._init:
2460                 self.pool._init_parent[self._name]=True
2461             else:
2462                 for id in ids:
2463                     # Find Position of the element
2464                     if vals[self._parent_name]:
2465                         cr.execute('select parent_left,parent_right,id from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (vals[self._parent_name],))
2466                     else:
2467                         cr.execute('select parent_left,parent_right,id from '+self._table+' where '+self._parent_name+' is null order by '+(self._parent_order or self._order))
2468                     result_p = cr.fetchall()
2469                     position = None
2470                     for (pleft,pright,pid) in result_p:
2471                         if pid == id:
2472                             break
2473                         position = pright+1
2474
2475                     # It's the first node of the parent: position = parent_left+1
2476                     if not position:
2477                         if not vals[self._parent_name]:
2478                             position = 1
2479                         else:
2480                             cr.execute('select parent_left from '+self._table+' where id=%s', (vals[self._parent_name],))
2481                             position = cr.fetchone()[0]+1
2482
2483                     # We have the new position !
2484                     cr.execute('select parent_left,parent_right from '+self._table+' where id=%s', (id,))
2485                     pleft,pright = cr.fetchone()
2486                     distance = pright - pleft + 1
2487
2488                     if position>pleft and position<=pright:
2489                         raise except_orm(_('UserError'), _('Recursivity Detected.'))
2490
2491                     if pleft<position:
2492                         cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position))
2493                         cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position))
2494                         cr.execute('update '+self._table+' set parent_left=parent_left+%s, parent_right=parent_right+%s where parent_left>=%s and parent_left<%s', (position-pleft,position-pleft, pleft, pright))
2495                     else:
2496                         cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position))
2497                         cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position))
2498                         cr.execute('update '+self._table+' set parent_left=parent_left-%s, parent_right=parent_right-%s where parent_left>=%s and parent_left<%s', (pleft-position+distance,pleft-position+distance, pleft+distance, pright+distance))
2499
2500         result = self._store_get_values(cr, user, ids, vals.keys(), context)
2501         for order, object, ids, fields in result:
2502             self.pool.get(object)._store_set_values(cr, user, ids, fields, context)
2503
2504         wf_service = netsvc.LocalService("workflow")
2505         for id in ids:
2506             wf_service.trg_write(user, self._name, id, cr)
2507         return True
2508
2509     #
2510     # TODO: Should set perm to user.xxx
2511     #
2512     def create(self, cr, user, vals, context=None):
2513         """ create(cr, user, vals, context) -> int
2514         cr = database cursor
2515         user = user id
2516         vals = dictionary of the form {'field_name':field_value, ...}
2517         """
2518         if not context:
2519             context = {}
2520         self.pool.get('ir.model.access').check(cr, user, self._name, 'create')
2521
2522         default = []
2523
2524         avoid_table = []
2525         for (t, c) in self._inherits.items():
2526             if c in vals:
2527                 avoid_table.append(t)
2528         for f in self._columns.keys(): # + self._inherit_fields.keys():
2529             if not f in vals:
2530                 default.append(f)
2531
2532         for f in self._inherit_fields.keys():
2533             if (not f in vals) and (self._inherit_fields[f][0] not in avoid_table):
2534                 default.append(f)
2535
2536         if len(default):
2537             default_values = self.default_get(cr, user, default, context)
2538             for dv in default_values:
2539                 if dv in self._columns and self._columns[dv]._type == 'many2many':
2540                     if default_values[dv] and isinstance(default_values[dv][0], (int, long)):
2541                         default_values[dv] = [(6, 0, default_values[dv])]
2542
2543             vals.update(default_values)
2544
2545         tocreate = {}
2546         for v in self._inherits:
2547             if self._inherits[v] not in vals:
2548                 tocreate[v] = {}
2549
2550         (upd0, upd1, upd2) = ('', '', [])
2551         upd_todo = []
2552
2553         for v in vals.keys():
2554             if v in self._inherit_fields:
2555                 (table, col, col_detail) = self._inherit_fields[v]
2556                 tocreate[table][v] = vals[v]
2557                 del vals[v]
2558
2559         # Try-except added to filter the creation of those records whose filds are readonly.
2560         # Example : any dashboard which has all the fields readonly.(due to Views(database views))
2561         try:
2562             cr.execute("SELECT nextval('"+self._sequence+"')")
2563         except:
2564             raise except_orm(_('UserError'),
2565                         _('You cannot perform this operation.'))
2566
2567         id_new = cr.fetchone()[0]
2568         for table in tocreate:
2569             id = self.pool.get(table).create(cr, user, tocreate[table])
2570             upd0 += ','+self._inherits[table]
2571             upd1 += ',%s'
2572             upd2.append(id)
2573         
2574         #Start : Set bool fields to be False if they are not touched(to make search more powerful) 
2575         bool_fields = [x for x in self._columns.keys() if self._columns[x]._type=='boolean']
2576         
2577         for bool_field in bool_fields:
2578             if bool_field not in vals:
2579                 vals[bool_field] = False
2580         #End
2581         
2582         for field in vals:
2583             if self._columns[field]._classic_write:
2584                 upd0 = upd0 + ',"' + field + '"'
2585                 upd1 = upd1 + ',' + self._columns[field]._symbol_set[0]
2586                 upd2.append(self._columns[field]._symbol_set[1](vals[field]))
2587             else:
2588                 upd_todo.append(field)
2589             if field in self._columns \
2590                     and hasattr(self._columns[field], 'selection') \
2591                     and vals[field]:
2592                 if self._columns[field]._type == 'reference':
2593                     val = vals[field].split(',')[0]
2594                 else:
2595                     val = vals[field]
2596                 if isinstance(self._columns[field].selection, (tuple, list)):
2597                     if val not in dict(self._columns[field].selection):
2598                         raise except_orm(_('ValidateError'),
2599                         _('The value "%s" for the field "%s" is not in the selection') \
2600                                 % (vals[field], field))
2601                 else:
2602                     if val not in dict(self._columns[field].selection(
2603                         self, cr, user, context=context)):
2604                         raise except_orm(_('ValidateError'),
2605                         _('The value "%s" for the field "%s" is not in the selection') \
2606                                 % (vals[field], field))
2607         if self._log_access:
2608             upd0 += ',create_uid,create_date'
2609             upd1 += ',%s,now()'
2610             upd2.append(user)
2611         cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2))
2612         upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
2613
2614         if self._parent_store:
2615             if self.pool._init:
2616                 self.pool._init_parent[self._name]=True
2617             else:
2618                 parent = vals.get(self._parent_name, False)
2619                 if parent:
2620                     cr.execute('select parent_right from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (parent,))
2621                     pleft_old = None
2622                     result_p = cr.fetchall()
2623                     for (pleft,) in result_p:
2624                         if not pleft:
2625                             break
2626                         pleft_old = pleft
2627                     if not pleft_old:
2628                         cr.execute('select parent_left from '+self._table+' where id=%s', (parent,))
2629                         pleft_old = cr.fetchone()[0]
2630                     pleft = pleft_old
2631                 else:
2632                     cr.execute('select max(parent_right) from '+self._table)
2633                     pleft = cr.fetchone()[0] or 0
2634                 cr.execute('update '+self._table+' set parent_left=parent_left+2 where parent_left>%s', (pleft,))
2635                 cr.execute('update '+self._table+' set parent_right=parent_right+2 where parent_right>%s', (pleft,))
2636                 cr.execute('update '+self._table+' set parent_left=%s,parent_right=%s where id=%s', (pleft+1,pleft+2,id_new))
2637                 
2638         # default element in context must be remove when call a one2many or many2many
2639         rel_context = context.copy()
2640         for c in context.items():
2641             if c[0].startswith('default_'):
2642                 del rel_context[c[0]]
2643
2644         for field in upd_todo:
2645             self._columns[field].set(cr, self, id_new, field, vals[field], user, rel_context)
2646         self._validate(cr, user, [id_new], context)
2647
2648         result = self._store_get_values(cr, user, [id_new], vals.keys(), context)
2649         for order, object, ids, fields in result:
2650             self.pool.get(object)._store_set_values(cr, user, ids, fields, context)
2651
2652         wf_service = netsvc.LocalService("workflow")
2653         wf_service.trg_create(user, self._name, id_new, cr)
2654         return id_new
2655
2656     def _store_get_values(self, cr, uid, ids, fields, context):
2657         result = {}
2658         fncts = self.pool._store_function.get(self._name, [])
2659         for fnct in range(len(fncts)):
2660             result.setdefault(fncts[fnct][0], {})
2661             ids2 = fncts[fnct][2](self,cr, uid, ids, context)
2662             for id in filter(None, ids2):
2663                 result[fncts[fnct][0]].setdefault(id, [])
2664                 result[fncts[fnct][0]][id].append(fnct)
2665         dict = {}
2666         for object in result:
2667             k2 = {}
2668             for id,fnct in result[object].items():
2669                 k2.setdefault(tuple(fnct), [])
2670                 k2[tuple(fnct)].append(id)
2671             for fnct,id in k2.items():
2672                 dict.setdefault(fncts[fnct[0]][4],[])
2673                 dict[fncts[fnct[0]][4]].append((fncts[fnct[0]][4],object,id,map(lambda x: fncts[x][1], fnct)))
2674         result2 = []
2675         tmp = dict.keys()
2676         tmp.sort()
2677         for k in tmp:
2678             result2+=dict[k]
2679         return result2
2680
2681     def _store_set_values(self, cr, uid, ids, fields, context):
2682         todo = {}
2683         keys = []
2684         for f in fields:
2685             if self._columns[f]._multi not in keys:
2686                 keys.append(self._columns[f]._multi)
2687             todo.setdefault(self._columns[f]._multi, [])
2688             todo[self._columns[f]._multi].append(f)
2689         for key in keys:
2690             val = todo[key]
2691             if key:
2692                 result = self._columns[val[0]].get(cr, self, ids, val, uid, context=context)
2693                 for id,value in result.items():
2694                     upd0 = []
2695                     upd1 = []
2696                     for v in value:
2697                         if v not in val:
2698                             continue
2699                         if self._columns[v]._type in ('many2one', 'one2one'):
2700                             try:
2701                                 value[v] = value[v][0]
2702                             except:
2703                                 pass
2704                         upd0.append('"'+v+'"='+self._columns[v]._symbol_set[0])
2705                         upd1.append(self._columns[v]._symbol_set[1](value[v]))
2706                     upd1.append(id)
2707                     cr.execute('update "' + self._table + '" set ' + \
2708                         string.join(upd0, ',') + ' where id = %s', upd1)
2709
2710             else:
2711                 for f in val:
2712                     result = self._columns[f].get(cr, self, ids, f, uid, context=context)
2713                     for id,value in result.items():
2714                         if self._columns[f]._type in ('many2one', 'one2one'):
2715                             try:
2716                                 value = value[0]
2717                             except:
2718                                 pass
2719                         cr.execute('update "' + self._table + '" set ' + \
2720                             '"'+f+'"='+self._columns[f]._symbol_set[0] + ' where id = %s', (self._columns[f]._symbol_set[1](value),id))
2721         return True
2722
2723     #
2724     # TODO: Validate
2725     #
2726     def perm_write(self, cr, user, ids, fields, context=None):
2727         raise _('This method does not exist anymore')
2728
2729     # TODO: ameliorer avec NULL
2730     def _where_calc(self, cr, user, args, active_test=True, context=None):
2731         if not context:
2732             context = {}
2733         args = args[:]
2734         # if the object has a field named 'active', filter out all inactive
2735         # records unless they were explicitely asked for
2736         if 'active' in self._columns and (active_test and context.get('active_test', True)):
2737             if args:
2738                 active_in_args = False
2739                 for a in args:
2740                     if a[0] == 'active':
2741                         active_in_args = True
2742                 if not active_in_args:
2743                     args.insert(0, ('active', '=', 1))
2744             else:
2745                 args = [('active', '=', 1)]
2746
2747         if args:
2748             import expression
2749             e = expression.expression(args)
2750             e.parse(cr, user, self, context)
2751             tables = e.get_tables()
2752             qu1, qu2 = e.to_sql()
2753             qu1 = qu1 and [qu1] or []
2754         else:
2755             qu1, qu2, tables = [], [], ['"%s"' % self._table]
2756
2757         return (qu1, qu2, tables)
2758
2759     def _check_qorder(self, word):
2760         if not regex_order.match(word):
2761             raise except_orm(_('AccessError'), _('Bad query.'))
2762         return True
2763
2764     def search(self, cr, user, args, offset=0, limit=None, order=None,
2765             context=None, count=False):
2766         if not context:
2767             context = {}
2768         # compute the where, order by, limit and offset clauses
2769         (qu1, qu2, tables) = self._where_calc(cr, user, args, context=context)
2770
2771         if len(qu1):
2772             qu1 = ' where '+string.join(qu1, ' and ')
2773         else:
2774             qu1 = ''
2775
2776         if order:
2777             self._check_qorder(order)
2778         order_by = order or self._order
2779
2780         limit_str = limit and ' limit %d' % limit or ''
2781         offset_str = offset and ' offset %d' % offset or ''
2782
2783
2784         # construct a clause for the rules :
2785         d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
2786         if d1:
2787             qu1 = qu1 and qu1+' and '+d1 or ' where '+d1
2788             qu2 += d2
2789
2790         if count:
2791             cr.execute('select count(%s.id) from ' % self._table +
2792                     ','.join(tables) +qu1 + limit_str + offset_str, qu2)
2793             res = cr.fetchall()
2794             return res[0][0]
2795         # execute the "main" query to fetch the ids we were searching for
2796         cr.execute('select %s.id from ' % self._table + ','.join(tables) +qu1+' order by '+order_by+limit_str+offset_str, qu2)
2797         res = cr.fetchall()
2798         return [x[0] for x in res]
2799
2800     # returns the different values ever entered for one field
2801     # this is used, for example, in the client when the user hits enter on
2802     # a char field
2803     def distinct_field_get(self, cr, uid, field, value, args=None, offset=0, limit=None):
2804         if not args:
2805             args = []
2806         if field in self._inherit_fields:
2807             return self.pool.get(self._inherit_fields[field][0]).distinct_field_get(cr, uid, field, value, args, offset, limit)
2808         else:
2809             return self._columns[field].search(cr, self, args, field, value, offset, limit, uid)
2810
2811     def name_get(self, cr, user, ids, context=None):
2812         if not context:
2813             context = {}
2814         if not ids:
2815             return []
2816         if isinstance(ids, (int, long)):
2817             ids = [ids]
2818         return [(r['id'], tools.ustr(r[self._rec_name])) for r in self.read(cr, user, ids,
2819             [self._rec_name], context, load='_classic_write')]
2820
2821     def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=None):
2822         if not args:
2823             args = []
2824         if not context:
2825             context = {}
2826         args = args[:]
2827         if name:
2828             args += [(self._rec_name, operator, name)]
2829         ids = self.search(cr, user, args, limit=limit, context=context)
2830         res = self.name_get(cr, user, ids, context)
2831         return res
2832
2833     def copy_data(self, cr, uid, id, default=None, context=None):
2834         if not context:
2835             context = {}
2836         if not default:
2837             default = {}
2838         if 'state' not in default:
2839             if 'state' in self._defaults:
2840                 default['state'] = self._defaults['state'](self, cr, uid, context)
2841         data = self.read(cr, uid, [id], context=context)[0]
2842         fields = self.fields_get(cr, uid)
2843         trans_data=[]
2844         for f in fields:
2845             ftype = fields[f]['type']
2846
2847             if self._log_access and f in ('create_date', 'create_uid', 'write_date', 'write_uid'):
2848                 del data[f]
2849
2850             if f in default:
2851                 data[f] = default[f]
2852             elif ftype == 'function':
2853                 del data[f]
2854             elif ftype == 'many2one':
2855                 try:
2856                     data[f] = data[f] and data[f][0]
2857                 except:
2858                     pass
2859             elif ftype in ('one2many', 'one2one'):
2860                 res = []
2861                 rel = self.pool.get(fields[f]['relation'])
2862                 for rel_id in data[f]:
2863                     # the lines are first duplicated using the wrong (old)
2864                     # parent but then are reassigned to the correct one thanks
2865                     # to the (4, ...)
2866                     d,t = rel.copy_data(cr, uid, rel_id, context=context)
2867                     res.append((0, 0, d))
2868                     trans_data += t
2869                 data[f] = res
2870             elif ftype == 'many2many':
2871                 data[f] = [(6, 0, data[f])]
2872
2873         trans_obj = self.pool.get('ir.translation')
2874         trans_name=''
2875         for f in fields:
2876             trans_flag=True
2877             if f in self._columns and self._columns[f].translate:
2878                 trans_name=self._name+","+f
2879             elif f in self._inherit_fields and self._inherit_fields[f][2].translate:
2880                 trans_name=self._inherit_fields[f][0]+","+f
2881             else:
2882                 trans_flag=False
2883
2884             if trans_flag:
2885                 trans_ids = trans_obj.search(cr, uid, [
2886                         ('name', '=', trans_name),
2887                         ('res_id','=',data['id'])
2888                     ])
2889
2890                 trans_data.extend(trans_obj.read(cr,uid,trans_ids,context=context))
2891
2892         del data['id']
2893
2894         for v in self._inherits:
2895             del data[self._inherits[v]]
2896         return data, trans_data
2897
2898     def copy(self, cr, uid, id, default=None, context=None):
2899         trans_obj = self.pool.get('ir.translation')
2900         data, trans_data = self.copy_data(cr, uid, id, default, context)
2901         new_id=self.create(cr, uid, data)
2902         for record in trans_data:
2903             del record['id']
2904             record['res_id']=new_id
2905             trans_obj.create(cr,uid,record)
2906         return new_id
2907
2908     def exists(self, cr, uid, id, context=None):
2909         cr.execute('SELECT count(1) FROM "%s" where id=%%s' % (self._table,), (id,))
2910         return bool(cr.fetchone()[0])
2911
2912     def check_recursion(self, cr, uid, ids, parent=None):
2913         if not parent:
2914             parent = self._parent_name
2915         ids_parent = ids[:]
2916         while len(ids_parent):
2917             ids_parent2 = []
2918             for i in range(0, len(ids), cr.IN_MAX):
2919                 sub_ids_parent = ids_parent[i:i+cr.IN_MAX]
2920                 cr.execute('SELECT distinct "'+parent+'"'+
2921                     ' FROM "'+self._table+'" ' \
2922                     'WHERE id in ('+','.join(map(str, sub_ids_parent))+')')
2923                 ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall())))
2924             ids_parent = ids_parent2
2925             for i in ids_parent:
2926                 if i in ids:
2927                     return False
2928         return True
2929
2930
2931 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
2932