Launchpad automatic translations update.
[odoo/odoo.git] / bin / osv / fields.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 # . Fields:
23 #      - simple
24 #      - relations (one2many, many2one, many2many)
25 #      - function
26 #
27 # Fields Attributes:
28 #   _classic_read: is a classic sql fields
29 #   _type   : field type
30 #   readonly
31 #   required
32 #   size
33 #
34 import string
35 import netsvc
36 import sys
37
38 from psycopg2 import Binary
39 import warnings
40
41 import tools
42
43
44 def _symbol_set(symb):
45     if symb == None or symb == False:
46         return None
47     elif isinstance(symb, unicode):
48         return symb.encode('utf-8')
49     return str(symb)
50
51
52 class _column(object):
53     _classic_read = True
54     _classic_write = True
55     _prefetch = True
56     _properties = False
57     _type = 'unknown'
58     _obj = None
59     _multi = False
60     _symbol_c = '%s'
61     _symbol_f = _symbol_set
62     _symbol_set = (_symbol_c, _symbol_f)
63     _symbol_get = None
64
65     def __init__(self, string='unknown', required=False, readonly=False, domain=None, context='', states=None, priority=0, change_default=False, size=None, ondelete="set null", translate=False, select=False, **args):
66         self.states = states or {}
67         self.string = string
68         self.readonly = readonly
69         self.required = required
70         self.size = size
71         self.help = args.get('help', '')
72         self.priority = priority
73         self.change_default = change_default
74         self.ondelete = ondelete
75         self.translate = translate
76         self._domain = domain or []
77         self._context = context
78         self.write = False
79         self.read = False
80         self.view_load = 0
81         self.select = select
82         self.selectable = True
83         self.group_operator = args.get('group_operator', False)
84         for a in args:
85             if args[a]:
86                 setattr(self, a, args[a])
87
88     def restart(self):
89         pass
90
91     def set(self, cr, obj, id, name, value, user=None, context=None):
92         cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%s', (self._symbol_set[1](value), id))
93
94     def set_memory(self, cr, obj, id, name, value, user=None, context=None):
95         raise Exception(_('Not implemented set_memory method !'))
96
97     def get_memory(self, cr, obj, ids, name, user=None, context=None, values=None):
98         raise Exception(_('Not implemented get_memory method !'))
99
100     def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
101         raise Exception(_('undefined get method !'))
102
103     def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None):
104         ids = obj.search(cr, uid, args+self._domain+[(name, 'ilike', value)], offset, limit, context=context)
105         res = obj.read(cr, uid, ids, [name], context=context)
106         return [x[name] for x in res]
107
108     def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None):
109         raise Exception(_('Not implemented search_memory method !'))
110
111
112 # ---------------------------------------------------------
113 # Simple fields
114 # ---------------------------------------------------------
115 class boolean(_column):
116     _type = 'boolean'
117     _symbol_c = '%s'
118     _symbol_f = lambda x: x and 'True' or 'False'
119     _symbol_set = (_symbol_c, _symbol_f)
120
121 class integer_big(_column):
122     _type = 'integer_big'
123     _symbol_c = '%s'
124     _symbol_f = lambda x: int(x or 0)
125     _symbol_set = (_symbol_c, _symbol_f)
126     _symbol_get = lambda self,x: x or 0
127
128 class integer(_column):
129     _type = 'integer'
130     _symbol_c = '%s'
131     _symbol_f = lambda x: int(x or 0)
132     _symbol_set = (_symbol_c, _symbol_f)
133     _symbol_get = lambda self,x: x or 0
134
135
136 class reference(_column):
137     _type = 'reference'
138     def __init__(self, string, selection, size, **args):
139         _column.__init__(self, string=string, size=size, selection=selection, **args)
140
141
142 class char(_column):
143     _type = 'char'
144
145     def __init__(self, string, size, **args):
146         _column.__init__(self, string=string, size=size, **args)
147         self._symbol_set = (self._symbol_c, self._symbol_set_char)
148
149     # takes a string (encoded in utf8) and returns a string (encoded in utf8)
150     def _symbol_set_char(self, symb):
151         #TODO:
152         # * we need to remove the "symb==False" from the next line BUT
153         #   for now too many things rely on this broken behavior
154         # * the symb==None test should be common to all data types
155         if symb == None or symb == False:
156             return None
157
158         # we need to convert the string to a unicode object to be able
159         # to evaluate its length (and possibly truncate it) reliably
160         u_symb = tools.ustr(symb)
161
162         return u_symb[:self.size].encode('utf8')
163
164
165 class text(_column):
166     _type = 'text'
167
168 import __builtin__
169
170 class float(_column):
171     _type = 'float'
172     _symbol_c = '%s'
173     _symbol_f = lambda x: __builtin__.float(x or 0.0)
174     _symbol_set = (_symbol_c, _symbol_f)
175     _symbol_get = lambda self,x: x or 0.0
176
177     def __init__(self, string='unknown', digits=None, digits_compute=None, **args):
178         _column.__init__(self, string=string, **args)
179         self.digits = digits
180         self.digits_compute = digits_compute
181
182
183     def digits_change(self, cr):
184         if self.digits_compute:
185             t = self.digits_compute(cr)
186             self._symbol_set=('%s', lambda x: ('%.'+str(t[1])+'f') % (__builtin__.float(x or 0.0),))
187             self.digits = t
188
189 class date(_column):
190     _type = 'date'
191
192
193 class datetime(_column):
194     _type = 'datetime'
195
196
197 class time(_column):
198     _type = 'time'
199
200 class binary(_column):
201     _type = 'binary'
202     _symbol_c = '%s'
203     _symbol_f = lambda symb: symb and Binary(symb) or None
204     _symbol_set = (_symbol_c, _symbol_f)
205     _symbol_get = lambda self, x: x and str(x)
206
207     _classic_read = False
208     _prefetch = False
209
210     def __init__(self, string='unknown', filters=None, **args):
211         _column.__init__(self, string=string, **args)
212         self.filters = filters
213
214     def get_memory(self, cr, obj, ids, name, user=None, context=None, values=None):
215         if not context:
216             context = {}
217         if not values:
218             values = []
219         res = {}
220         for i in ids:
221             val = None
222             for v in values:
223                 if v['id'] == i:
224                     val = v[name]
225                     break
226             if context.get('bin_size', False) and val:
227                 res[i] = tools.human_size(long(val))
228             else:
229                 res[i] = val
230         return res
231
232     get = get_memory
233
234
235 class selection(_column):
236     _type = 'selection'
237
238     def __init__(self, selection, string='unknown', **args):
239         _column.__init__(self, string=string, **args)
240         self.selection = selection
241
242 # ---------------------------------------------------------
243 # Relationals fields
244 # ---------------------------------------------------------
245
246 #
247 # Values: (0, 0,  { fields })    create
248 #         (1, ID, { fields })    modification
249 #         (2, ID)                remove (delete)
250 #         (3, ID)                unlink one (target id or target of relation)
251 #         (4, ID)                link
252 #         (5)                    unlink all (only valid for one2many)
253 #
254 #CHECKME: dans la pratique c'est quoi la syntaxe utilisee pour le 5? (5) ou (5, 0)?
255 class one2one(_column):
256     _classic_read = False
257     _classic_write = True
258     _type = 'one2one'
259
260     def __init__(self, obj, string='unknown', **args):
261         warnings.warn("The one2one field doesn't work anymore", DeprecationWarning)
262         _column.__init__(self, string=string, **args)
263         self._obj = obj
264
265     def set(self, cr, obj_src, id, field, act, user=None, context=None):
266         if not context:
267             context = {}
268         obj = obj_src.pool.get(self._obj)
269         self._table = obj_src.pool.get(self._obj)._table
270         if act[0] == 0:
271             id_new = obj.create(cr, user, act[1])
272             cr.execute('update '+obj_src._table+' set '+field+'=%s where id=%s', (id_new, id))
273         else:
274             cr.execute('select '+field+' from '+obj_src._table+' where id=%s', (act[0],))
275             id = cr.fetchone()[0]
276             obj.write(cr, user, [id], act[1], context=context)
277
278     def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None):
279         return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', 'like', value)], offset, limit, context=context)
280
281
282 class many2one(_column):
283     _classic_read = False
284     _classic_write = True
285     _type = 'many2one'
286     _symbol_c = '%s'
287     _symbol_f = lambda x: x or None
288     _symbol_set = (_symbol_c, _symbol_f)
289
290     def __init__(self, obj, string='unknown', **args):
291         _column.__init__(self, string=string, **args)
292         self._obj = obj
293
294     def set_memory(self, cr, obj, id, field, values, user=None, context=None):
295         obj.datas.setdefault(id, {})
296         obj.datas[id][field] = values
297
298     def get_memory(self, cr, obj, ids, name, user=None, context=None, values=None):
299         result = {}
300         for id in ids:
301             result[id] = obj.datas[id][name]
302         return result
303
304     def get(self, cr, obj, ids, name, user=None, context=None, values=None):
305         if not context:
306             context = {}
307         if not values:
308             values = {}
309         res = {}
310         for r in values:
311             res[r['id']] = r[name]
312         for id in ids:
313             res.setdefault(id, '')
314         obj = obj.pool.get(self._obj)
315
316         # build a dictionary of the form {'id_of_distant_resource': name_of_distant_resource}
317         from orm import except_orm
318         names = {}
319         for record in list(set(filter(None, res.values()))):
320             try:
321                 record_name = dict(obj.name_get(cr, user, [record], context))
322             except except_orm:
323                 record_name = {}
324                 record_name[record] = '// Access Denied //'
325             names.update(record_name)
326
327         for r in res.keys():
328             if res[r] and res[r] in names:
329                 res[r] = (res[r], names[res[r]])
330             else:
331                 res[r] = False
332         return res
333
334     def set(self, cr, obj_src, id, field, values, user=None, context=None):
335         if not context:
336             context = {}
337         obj = obj_src.pool.get(self._obj)
338         self._table = obj_src.pool.get(self._obj)._table
339         if type(values) == type([]):
340             for act in values:
341                 if act[0] == 0:
342                     id_new = obj.create(cr, act[2])
343                     cr.execute('update '+obj_src._table+' set '+field+'=%s where id=%s', (id_new, id))
344                 elif act[0] == 1:
345                     obj.write(cr, [act[1]], act[2], context=context)
346                 elif act[0] == 2:
347                     cr.execute('delete from '+self._table+' where id=%s', (act[1],))
348                 elif act[0] == 3 or act[0] == 5:
349                     cr.execute('update '+obj_src._table+' set '+field+'=null where id=%s', (id,))
350                 elif act[0] == 4:
351                     cr.execute('update '+obj_src._table+' set '+field+'=%s where id=%s', (act[1], id))
352         else:
353             if values:
354                 cr.execute('update '+obj_src._table+' set '+field+'=%s where id=%s', (values, id))
355             else:
356                 cr.execute('update '+obj_src._table+' set '+field+'=null where id=%s', (id,))
357
358     def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None):
359         return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', 'like', value)], offset, limit, context=context)
360
361
362 class one2many(_column):
363     _classic_read = False
364     _classic_write = False
365     _prefetch = False
366     _type = 'one2many'
367
368     def __init__(self, obj, fields_id, string='unknown', limit=None, **args):
369         _column.__init__(self, string=string, **args)
370         self._obj = obj
371         self._fields_id = fields_id
372         self._limit = limit
373         #one2many can't be used as condition for defaults
374         assert(self.change_default != True)
375
376     def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
377         if not context:
378             context = {}
379         if self._context:
380             context = context.copy()
381             context.update(self._context)
382         if not values:
383             values = {}
384         res = {}
385         for id in ids:
386             res[id] = []
387         ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
388         for r in obj.pool.get(self._obj).read(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
389             if r[self._fields_id] in res:
390                 res[r[self._fields_id]].append(r['id'])
391         return res
392
393     def set_memory(self, cr, obj, id, field, values, user=None, context=None):
394         if not context:
395             context = {}
396         if self._context:
397             context = context.copy()
398         context.update(self._context)
399         if not values:
400             return
401         obj = obj.pool.get(self._obj)
402         for act in values:
403             if act[0] == 0:
404                 act[2][self._fields_id] = id
405                 obj.create(cr, user, act[2], context=context)
406             elif act[0] == 1:
407                 obj.write(cr, user, [act[1]], act[2], context=context)
408             elif act[0] == 2:
409                 obj.unlink(cr, user, [act[1]], context=context)
410             elif act[0] == 3:
411                 obj.datas[act[1]][self._fields_id] = False
412             elif act[0] == 4:
413                 obj.datas[act[1]] = id
414             elif act[0] == 5:
415                 for o in obj.datas.values():
416                     if o[self._fields_id] == id:
417                         o[self._fields_id] = False
418             elif act[0] == 6:
419                 for id2 in (act[2] or []):
420                     obj.datas[id2][self._fields_id] = id
421
422     def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None):
423         raise _('Not Implemented')
424
425     def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
426         if not context:
427             context = {}
428         if self._context:
429             context = context.copy()
430         context.update(self._context)
431         if not values:
432             values = {}
433         res = {}
434         for id in ids:
435             res[id] = []
436         ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
437         for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
438             res[r[self._fields_id]].append(r['id'])
439         return res
440
441     def set(self, cr, obj, id, field, values, user=None, context=None):
442         result = []
443         if not context:
444             context = {}
445         if self._context:
446             context = context.copy()
447         context.update(self._context)
448         context['no_store_function'] = True
449         if not values:
450             return
451         _table = obj.pool.get(self._obj)._table
452         obj = obj.pool.get(self._obj)
453         for act in values:
454             if act[0] == 0:
455                 act[2][self._fields_id] = id
456                 id_new = obj.create(cr, user, act[2], context=context)
457                 result += obj._store_get_values(cr, user, [id_new], act[2].keys(), context)
458             elif act[0] == 1:
459                 obj.write(cr, user, [act[1]], act[2], context=context)
460             elif act[0] == 2:
461                 obj.unlink(cr, user, [act[1]], context=context)
462             elif act[0] == 3:
463                 cr.execute('update '+_table+' set '+self._fields_id+'=null where id=%s', (act[1],))
464             elif act[0] == 4:
465                 cr.execute('update '+_table+' set '+self._fields_id+'=%s where id=%s', (id, act[1]))
466             elif act[0] == 5:
467                 cr.execute('update '+_table+' set '+self._fields_id+'=null where '+self._fields_id+'=%s', (id,))
468             elif act[0] == 6:
469                 obj.write(cr, user, act[2], {self._fields_id:id}, context=context or {})
470                 ids2 = act[2] or [0]
471                 cr.execute('select id from '+_table+' where '+self._fields_id+'=%s and id <> ALL (%s)', (id,ids2))
472                 ids3 = map(lambda x:x[0], cr.fetchall())
473                 obj.write(cr, user, ids3, {self._fields_id:False}, context=context or {})
474         return result
475
476     def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None):
477         return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, operator, context=context,limit=limit)
478
479
480 #
481 # Values: (0, 0,  { fields })    create
482 #         (1, ID, { fields })    modification
483 #         (2, ID)                remove
484 #         (3, ID)                unlink
485 #         (4, ID)                link
486 #         (5, ID)                unlink all
487 #         (6, ?, ids)            set a list of links
488 #
489 class many2many(_column):
490     _classic_read = False
491     _classic_write = False
492     _prefetch = False
493     _type = 'many2many'
494
495     def __init__(self, obj, rel, id1, id2, string='unknown', limit=None, **args):
496         _column.__init__(self, string=string, **args)
497         self._obj = obj
498         if '.' in rel:
499             raise Exception(_('The second argument of the many2many field %s must be a SQL table !'\
500                 'You used %s, which is not a valid SQL table name.')% (string,rel))
501         self._rel = rel
502         self._id1 = id1
503         self._id2 = id2
504         self._limit = limit
505
506     def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
507         if not context:
508             context = {}
509         if not values:
510             values = {}
511         res = {}
512         if not ids:
513             return res
514         for id in ids:
515             res[id] = []
516         limit_str = self._limit is not None and ' limit %d' % self._limit or ''
517         obj = obj.pool.get(self._obj)
518
519         d1, d2, tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context)
520         if d1:
521             d1 = ' and ' + ' and '.join(d1)
522         else: d1 = ''
523
524         cr.execute('SELECT '+self._rel+'.'+self._id2+','+self._rel+'.'+self._id1+' \
525                 FROM '+self._rel+' , '+(','.join(tables))+' \
526                 WHERE '+self._rel+'.'+self._id1+' = ANY (%s) \
527                     AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+d1
528                 +limit_str+' order by '+obj._table+'.'+obj._order+' offset %s',
529                 [ids,]+d2+[offset])
530         for r in cr.fetchall():
531             res[r[1]].append(r[0])
532         return res
533
534     def set(self, cr, obj, id, name, values, user=None, context=None):
535         if not context:
536             context = {}
537         if not values:
538             return
539         obj = obj.pool.get(self._obj)
540         for act in values:
541             if not (isinstance(act, list) or isinstance(act, tuple)) or not act:
542                 continue
543             if act[0] == 0:
544                 idnew = obj.create(cr, user, act[2])
545                 cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s,%s)', (id, idnew))
546             elif act[0] == 1:
547                 obj.write(cr, user, [act[1]], act[2], context=context)
548             elif act[0] == 2:
549                 obj.unlink(cr, user, [act[1]], context=context)
550             elif act[0] == 3:
551                 cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%s and '+ self._id2 + '=%s', (id, act[1]))
552             elif act[0] == 4:
553                 cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s,%s)', (id, act[1]))
554             elif act[0] == 5:
555                 cr.execute('update '+self._rel+' set '+self._id2+'=null where '+self._id2+'=%s', (id,))
556             elif act[0] == 6:
557
558                 d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context)
559                 if d1:
560                     d1 = ' and ' + ' and '.join(d1)
561                 else:
562                     d1 = ''
563                 cr.execute('delete from '+self._rel+' where '+self._id1+'=%s AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+','.join(tables)+' WHERE '+self._rel+'.'+self._id1+'=%s AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2)
564
565                 for act_nbr in act[2]:
566                     cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s, %s)', (id, act_nbr))
567
568     #
569     # TODO: use a name_search
570     #
571     def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None):
572         return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', operator, value)], offset, limit, context=context)
573
574     def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
575         result = {}
576         for id in ids:
577             result[id] = obj.datas[id].get(name, [])
578         return result
579
580     def set_memory(self, cr, obj, id, name, values, user=None, context=None):
581         if not values:
582             return
583         for act in values:
584             # TODO: use constants instead of these magic numbers
585             if act[0] == 0:
586                 raise _('Not Implemented')
587             elif act[0] == 1:
588                 raise _('Not Implemented')
589             elif act[0] == 2:
590                 raise _('Not Implemented')
591             elif act[0] == 3:
592                 raise _('Not Implemented')
593             elif act[0] == 4:
594                 raise _('Not Implemented')
595             elif act[0] == 5:
596                 raise _('Not Implemented')
597             elif act[0] == 6:
598                 obj.datas[id][name] = act[2]
599
600
601 def get_nice_size(a):
602     (x,y) = a
603     if isinstance(y, (int,long)):
604         size = y
605     elif y:
606         size = len(y)
607     else:
608         size = 0
609     return (x, tools.human_size(size))
610
611 # ---------------------------------------------------------
612 # Function fields
613 # ---------------------------------------------------------
614 class function(_column):
615     _classic_read = False
616     _classic_write = False
617     _prefetch = False
618     _type = 'function'
619     _properties = True
620
621 #
622 # multi: compute several fields in one call
623 #
624     def __init__(self, fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float', fnct_search=None, obj=None, method=False, store=False, multi=False, **args):
625         _column.__init__(self, **args)
626         self._obj = obj
627         self._method = method
628         self._fnct = fnct
629         self._fnct_inv = fnct_inv
630         self._arg = arg
631         self._multi = multi
632         if 'relation' in args:
633             self._obj = args['relation']
634
635         self.digits = args.get('digits', (16,2))
636         self.digits_compute = args.get('digits_compute', None)
637
638         self._fnct_inv_arg = fnct_inv_arg
639         if not fnct_inv:
640             self.readonly = 1
641         self._type = type
642         self._fnct_search = fnct_search
643         self.store = store
644
645         if not fnct_search and not store:
646             self.selectable = False
647
648         if store:
649             self._classic_read = True
650             self._classic_write = True
651             if type=='binary':
652                 self._symbol_get=lambda x:x and str(x)
653
654         if type == 'float':
655             self._symbol_c = float._symbol_c
656             self._symbol_f = float._symbol_f
657             self._symbol_set = float._symbol_set
658
659     def digits_change(self, cr):
660         if self.digits_compute:
661             t = self.digits_compute(cr)
662             self._symbol_set=('%s', lambda x: ('%.'+str(t[1])+'f') % (__builtin__.float(x or 0.0),))
663             self.digits = t
664
665
666     def search(self, cr, uid, obj, name, args, context=None):
667         if not self._fnct_search:
668             #CHECKME: should raise an exception
669             return []
670         return self._fnct_search(obj, cr, uid, obj, name, args, context=context)
671
672     def get(self, cr, obj, ids, name, user=None, context=None, values=None):
673         if not context:
674             context = {}
675         if not values:
676             values = {}
677         res = {}
678         if self._method:
679             res = self._fnct(obj, cr, user, ids, name, self._arg, context)
680         else:
681             res = self._fnct(cr, obj._table, ids, name, self._arg, context)
682
683         if self._type == "many2one" :
684             # Filtering only integer/long values if passed
685             res_ids = [x for x in res.values() if x and isinstance(x, (int,long))]
686
687             if res_ids:
688                 obj_model = obj.pool.get(self._obj)
689                 dict_names = dict(obj_model.name_get(cr, user, res_ids, context))
690                 for r in res.keys():
691                     if res[r] and res[r] in dict_names:
692                         res[r] = (res[r], dict_names[res[r]])
693
694         if self._type == 'binary' and context.get('bin_size', False):
695             # convert the data returned by the function with the size of that data...
696             res = dict(map( get_nice_size, res.items()))
697         if self._type == "integer":
698             for r in res.keys():
699                 # Converting value into string so that it does not affect XML-RPC Limits
700                 if isinstance(res[r],dict): # To treat integer values with _multi attribute
701                     for record in res[r].keys():
702                         res[r][record] = str(res[r][record])
703                 else:
704                     res[r] = str(res[r])
705         return res
706     get_memory = get
707
708     def set(self, cr, obj, id, name, value, user=None, context=None):
709         if not context:
710             context = {}
711         if self._fnct_inv:
712             self._fnct_inv(obj, cr, user, id, name, value, self._fnct_inv_arg, context)
713     set_memory = set
714
715 # ---------------------------------------------------------
716 # Related fields
717 # ---------------------------------------------------------
718
719 class related(function):
720
721     def _fnct_search(self, tobj, cr, uid, obj=None, name=None, domain=None, context={}):
722         self._field_get2(cr, uid, obj, context)
723         i = len(self._arg)-1
724         sarg = name
725         while i>0:
726             if type(sarg) in [type([]), type( (1,) )]:
727                 where = [(self._arg[i], 'in', sarg)]
728             else:
729                 where = [(self._arg[i], '=', sarg)]
730             if domain:
731                 where = map(lambda x: (self._arg[i],x[1], x[2]), domain)
732                 domain = []
733             sarg = obj.pool.get(self._relations[i]['object']).search(cr, uid, where, context=context)
734             i -= 1
735         return [(self._arg[0], 'in', sarg)]
736
737     def _fnct_write(self,obj,cr, uid, ids, field_name, values, args, context=None):
738         if values and field_name:
739             self._field_get2(cr, uid, obj, context)
740             relation = obj._name
741             res = {}
742             if type(ids) != type([]):
743                 ids=[ids]
744             objlst = obj.browse(cr, uid, ids)
745             for data in objlst:
746                 t_id=None
747                 t_data = data
748                 relation = obj._name
749                 for i in range(len(self.arg)):
750                     field_detail = self._relations[i]
751                     relation = field_detail['object']
752                     if not t_data[self.arg[i]]:
753                         if self._type not in ('one2many', 'many2many'):
754                             t_id = t_data['id']
755                         t_data = False
756                         break
757                     if field_detail['type'] in ('one2many', 'many2many'):
758                         if self._type != "many2one":
759                             t_id = t_data.id
760                             t_data = t_data[self.arg[i]][0]
761                         else:
762                             t_data = False
763                             break
764                     else:
765                         t_id = t_data['id']
766                         t_data = t_data[self.arg[i]]
767
768                 if t_id and t_data:
769                     obj.pool.get(field_detail['object']).write(cr,uid,[t_id],{args[-1]:values}, context=context)
770
771     def _fnct_read(self, obj, cr, uid, ids, field_name, args, context=None):
772         self._field_get2(cr, uid, obj, context)
773         if not ids: return {}
774         relation = obj._name
775         res = {}.fromkeys(ids, False)
776
777         objlst = obj.browse(cr, uid, ids, context=context)
778         for data in objlst:
779             if not data:
780                 continue
781             t_data = data
782             relation = obj._name
783             for i in range(len(self.arg)):
784                 field_detail = self._relations[i]
785                 relation = field_detail['object']
786                 try:
787                     if not t_data[self.arg[i]]:
788                         t_data = False
789                         break
790                 except:
791                     t_data = False
792                     break
793                 if field_detail['type'] in ('one2many', 'many2many') and i != len(self.arg) - 1:
794                     t_data = t_data[self.arg[i]][0]
795                 else:
796                     t_data = t_data[self.arg[i]]
797             if type(t_data) == type(objlst[0]):
798                 res[data.id] = t_data.id
799             else:
800                 res[data.id] = t_data
801         if self._type=='many2one':
802             ids = filter(None, res.values())
803             if ids:
804                 ng = dict(obj.pool.get(self._obj).name_get(cr, uid, ids, context=context))
805                 for r in res:
806                     if res[r]:
807                         res[r] = (res[r], ng[res[r]])
808         elif self._type in ('one2many', 'many2many'):
809             for r in res:
810                 if res[r]:
811                     res[r] = [x.id for x in res[r]]
812         return res
813
814     def __init__(self, *arg, **args):
815         self.arg = arg
816         self._relations = []
817         super(related, self).__init__(self._fnct_read, arg, self._fnct_write, fnct_inv_arg=arg, method=True, fnct_search=self._fnct_search, **args)
818         if self.store is True:
819             # TODO: improve here to change self.store = {...} according to related objects
820             pass
821
822     def _field_get2(self, cr, uid, obj, context={}):
823         if self._relations:
824             return
825         obj_name = obj._name
826         for i in range(len(self._arg)):
827             f = obj.pool.get(obj_name).fields_get(cr, uid, [self._arg[i]], context=context)[self._arg[i]]
828             self._relations.append({
829                 'object': obj_name,
830                 'type': f['type']
831
832             })
833             if f.get('relation',False):
834                 obj_name = f['relation']
835                 self._relations[-1]['relation'] = f['relation']
836
837 # ---------------------------------------------------------
838 # Dummy fields
839 # ---------------------------------------------------------
840
841 class dummy(function):
842     def _fnct_search(self, tobj, cr, uid, obj=None, name=None, domain=None, context={}):
843         return []
844
845     def _fnct_write(self,obj,cr, uid, ids, field_name, values, args, context=None):
846         return False
847
848     def _fnct_read(self, obj, cr, uid, ids, field_name, args, context=None):
849         return {}
850
851     def __init__(self, *arg, **args):
852         self.arg = arg
853         self._relations = []
854         super(dummy, self).__init__(self._fnct_read, arg, self._fnct_write, fnct_inv_arg=arg, method=True, fnct_search=None, **args)
855
856 # ---------------------------------------------------------
857 # Serialized fields
858 # ---------------------------------------------------------
859 class serialized(_column):
860     def __init__(self, string='unknown', serialize_func=repr, deserialize_func=eval, type='text', **args):
861         self._serialize_func = serialize_func
862         self._deserialize_func = deserialize_func
863         self._type = type
864         self._symbol_set = (self._symbol_c, self._serialize_func)
865         self._symbol_get = self._deserialize_func
866         super(serialized, self).__init__(string=string, **args)
867
868
869 class property(function):
870
871     def _fnct_write(self, obj, cr, uid, id, prop, id_val, val, context=None):
872         if not context:
873             context = {}
874         (obj_dest,) = val
875         definition_id = self._field_get(cr, uid, obj._name, prop)
876
877         property = obj.pool.get('ir.property')
878         nid = property.search(cr, uid, [('fields_id', '=', definition_id),
879             ('res_id', '=', obj._name+','+str(id))])
880         while len(nid):
881             cr.execute('DELETE FROM ir_property WHERE id=%s', (nid.pop(),))
882
883         nid = property.search(cr, uid, [('fields_id', '=', definition_id),
884             ('res_id', '=', False)])
885         default_val = False
886         if nid:
887             default_val = property.browse(cr, uid, nid[0], context).value
888
889         company_id = obj.pool.get('res.company')._company_default_get(cr, uid, obj._name, prop, context=context)
890         res = False
891         if val[0]:
892             newval = (id_val and obj_dest+','+str(id_val)) or False
893         else:
894             newval = id_val or False
895         if (newval != default_val) and newval:
896             propdef = obj.pool.get('ir.model.fields').browse(cr, uid,
897                     definition_id, context=context)
898             res = property.create(cr, uid, {
899                 'name': propdef.name,
900                 'value': newval,
901                 'res_id': obj._name+','+str(id),
902                 'company_id': company_id,
903                 'fields_id': definition_id
904             }, context=context)
905         return res
906
907     def _fnct_read(self, obj, cr, uid, ids, prop, val, context=None):
908         if not context:
909             context = {}
910         property = obj.pool.get('ir.property')
911         definition_id = self._field_get(cr, uid, obj._name, prop)
912
913         nid = property.search(cr, uid, [('fields_id', '=', definition_id),
914             ('res_id', '=', False)])
915         default_val = False
916         if nid:
917             d = property.browse(cr, uid, nid[0], context).value
918             default_val = (d and int(d.split(',')[1])) or False
919
920         vids = [obj._name + ',' + str(id) for id in  ids]
921         nids = property.search(cr, uid, [('fields_id', '=', definition_id),
922             ('res_id', 'in', vids)])
923
924         res = {}
925         for id in ids:
926             res[id] = default_val
927         for prop in property.browse(cr, uid, nids):
928             if prop.value.find(',') >= 0:
929                 res[int(prop.res_id.id)] = (prop.value and \
930                         int(prop.value.split(',')[1])) or False
931             else:
932                 res[int(prop.res_id.id)] = prop.value or ''
933
934         if self._obj:
935             obj = obj.pool.get(self._obj)
936             to_check = res.values()
937             if default_val and default_val not in to_check:
938                 to_check += [default_val]
939             existing_ids = obj.search(cr, uid, [('id', 'in', to_check)])
940             for id, res_id in res.items():
941                 if res_id not in existing_ids:
942                     cr.execute('DELETE FROM ir_property WHERE value=%s', ((obj._name+','+str(res_id)),))
943                     res[id] = default_val
944             names = dict(obj.name_get(cr, uid, existing_ids, context))
945             for r in res.keys():
946                 if res[r] and res[r] in names:
947                     res[r] = (res[r], names[res[r]])
948                 else:
949                     res[r] = False
950         return res
951
952     def _field_get(self, cr, uid, model_name, prop):
953         if not self.field_id.get(cr.dbname):
954             cr.execute('SELECT id \
955                     FROM ir_model_fields \
956                     WHERE name=%s AND model=%s', (prop, model_name))
957             res = cr.fetchone()
958             self.field_id[cr.dbname] = res and res[0]
959         return self.field_id[cr.dbname]
960
961     def __init__(self, obj_prop, **args):
962         self.field_id = {}
963         function.__init__(self, self._fnct_read, False, self._fnct_write,
964                 (obj_prop, ), **args)
965
966     def restart(self):
967         self.field_id = {}
968
969
970 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
971