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