[FIX] OPW 18442 : View location don't consolidate child, actualy any location with...
[odoo/odoo.git] / addons / base_module_record / base_module_record.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 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 from xml.dom import minidom
23 from osv.osv import osv_pool
24 from osv import fields,osv
25 import pooler
26 import string
27 import tools
28
29 class recording_objects_proxy(osv_pool):
30     def execute(self, *args, **argv):
31         if args[3] == 'create':
32             _old_args = args[4].copy()
33         elif args[3] == 'write':
34             _old_args = args[5].copy()
35         elif len(args) >= 5 and isinstance(args[4], dict):
36             _old_args = args[4].copy()
37         elif len(args) > 5 and args[3] != 'write' and isinstance(args[5], dict):
38             _old_args = args[5].copy()
39         else:
40             _old_args = None
41         res = super(recording_objects_proxy, self).execute(*args, **argv)
42         pool = pooler.get_pool(args[0])
43         mod = pool.get('ir.module.record')
44
45         if mod and mod.recording:
46             if args[3] not in ('default_get','read','fields_view_get','fields_get','search','search_count','name_search','name_get','get','request_get', 'get_sc', 'unlink'):
47                 if _old_args is not None:
48                     if args[3] != 'write' and args[3] != 'create' and len(args) > 5 and isinstance(args[5], dict):
49                        args=list(args) 
50                        args[5]=_old_args
51                        args=tuple(args)
52                        mod.recording_data.append(('osv_memory_action', args, argv ,None))
53                     else:
54                        if args[3] == 'create':
55                            args[4].update(_old_args)
56                        elif args[3] == 'write':
57                            args[5].update(_old_args)
58                        mod.recording_data.append(('query', args, argv,res))
59         return res
60
61     def exec_workflow(self, *args, **argv):
62         res = super(recording_objects_proxy, self).exec_workflow(*args, **argv)
63         pool = pooler.get_pool(args[0])
64         mod = pool.get('ir.module.record')
65         if mod and mod.recording:
66             mod.recording_data.append(('workflow', args, argv))
67         return res
68
69 recording_objects_proxy()
70
71 class xElement(minidom.Element):
72     """dom.Element with compact print
73     The Element in minidom has a problem: if printed, adds whitespace
74     around the text nodes. The standard will not ignore that whitespace.
75     This class simply prints the contained nodes in their compact form, w/o
76     added spaces.
77     """
78     def writexml(self, writer, indent="", addindent="", newl=""):
79         writer.write(indent)
80         minidom.Element.writexml(self, writer, indent='', addindent='', newl='')
81         writer.write(newl)
82
83 def doc_createXElement(xdoc, tagName):
84         e = xElement(tagName)
85         e.ownerDocument = xdoc
86         return e
87
88 import yaml
89 from tools import yaml_tag # This import is not unused! Do not remove!
90 # Please do not override yaml_tag here: modify it in server bin/tools/yaml_tag.py
91
92 class base_module_record(osv.osv):
93     _name = "ir.module.record"
94     _columns = {
95
96     }
97     def __init__(self, *args, **kwargs):
98         self.recording = 0
99         self.recording_data = []
100         self.depends = {}
101         super(base_module_record, self).__init__(*args, **kwargs)
102
103     # To Be Improved
104     def _create_id(self, cr, uid, model, data):
105         i = 0
106         while True:
107             try:
108                 name = filter(lambda x: x in string.letters, (data.get('name','') or '').lower())
109             except:
110                 name=''
111 #            name=data.get('name','') or ''.lower()
112             val = model.replace('.','_')+'_'+ name + str(i)
113             i+=1
114             if val not in self.ids.values():
115                 break
116         return val
117
118     def _get_id(self, cr, uid, model, id):
119         if type(id)==type(()):
120             id=id[0]
121         if (model,id) in self.ids:
122             res_id = self.ids[(model,id)]
123             return res_id, False
124         dt = self.pool.get('ir.model.data')
125         dtids = dt.search(cr, uid, [('model','=',model), ('res_id','=',id)])
126         if not dtids:
127             return False, None
128         obj = dt.browse(cr, uid, dtids[0])
129         self.depends[obj.module] = True
130         return obj.module+'.'+obj.name, obj.noupdate
131     
132     def _create_record(self, cr, uid, doc, model, data, record_id, noupdate=False):
133         data_pool = self.pool.get('ir.model.data')
134         model_pool = self.pool.get(model)
135         
136         record = doc.createElement('record')
137         record.setAttribute("id", record_id)
138         record.setAttribute("model", model)
139         record_list = [record]
140         
141         lids  = data_pool.search(cr, uid, [('model','=',model)])
142         res = data_pool.read(cr, uid, lids[:1], ['module'])
143         if res:
144             self.depends[res[0]['module']]=True
145         fields = model_pool.fields_get(cr, uid)
146         for key,val in data.items():
147             if not (val or (fields[key]['type']=='boolean')):
148                 continue
149             if (fields[key]['type'] in ('integer','float') or
150                 fields[key]['type'] == 'selection' and isinstance(val, int)):
151                 field = doc.createElement('field')
152                 field.setAttribute("name", key)
153                 field.setAttribute("eval", val and str(val) or 'False' )
154                 record.appendChild(field)
155             elif fields[key]['type'] in ('boolean',):
156                 field = doc.createElement('field')
157                 field.setAttribute("name", key)
158                 field.setAttribute("eval", val and '1' or '0' )
159                 record.appendChild(field)
160             elif fields[key]['type'] in ('many2one',):
161                 field = doc.createElement('field')
162                 field.setAttribute("name", key)
163                 if type(val) in (type(''),type(u'')):
164                     id = val
165                 else:
166                     id,update = self._get_id(cr, uid, fields[key]['relation'], val)
167                     noupdate = noupdate or update
168                 if not id:
169                     relation_pool = self.pool.get(fields[key]['relation'])
170                     
171                     field.setAttribute("model", fields[key]['relation'])
172                     fld_nm = relation_pool._rec_name
173                     name = relation_pool.read(cr, uid, val,[fld_nm])[fld_nm] or False
174                     field.setAttribute("search", str([(str(fld_nm) ,'=', name)]))
175                 else:
176                     field.setAttribute("ref", id)
177                 record.appendChild(field)
178             elif fields[key]['type'] in ('one2many',):
179                 for valitem in (val or []):
180                     if valitem[0] in (0,1):
181                         if key in model_pool._columns:
182                             model_pool._columns[key]._fields_id
183                         else:
184                             model_pool._inherit_fields[key][2]._fields_id
185                         if valitem[0] == 0:
186                             newid = self._create_id(cr, uid, fields[key]['relation'], valitem[2])
187                             valitem[1]=newid
188                         else:
189                             newid,update = self._get_id(cr, uid, fields[key]['relation'], valitem[1])
190                             if not newid:
191                                 newid = self._create_id(cr, uid, fields[key]['relation'], valitem[2])
192                                 valitem[1]=newid
193                         self.ids[(fields[key]['relation'], valitem[1])] = newid
194                         
195                         childrecord, update = self._create_record(cr, uid, doc, fields[key]['relation'],valitem[2], newid)
196                         noupdate = noupdate or update
197                         record_list += childrecord
198                     else:
199                         pass
200             elif fields[key]['type'] in ('many2many',):
201                 res = []
202                 for valitem in (val or []):
203                     if valitem[0]==6:
204                         for id2 in valitem[2]:
205                             id,update = self._get_id(cr, uid, fields[key]['relation'], id2)
206                             self.ids[(fields[key]['relation'],id2)] = id
207                             noupdate = noupdate or update
208                             res.append(id)
209                         field = doc.createElement('field')
210                         field.setAttribute("name", key)
211                         field.setAttribute("eval", "[(6,0,["+','.join(map(lambda x: "ref('%s')" % (x,), res))+'])]')
212                         record.appendChild(field)
213             else:
214                 field = doc_createXElement(doc, 'field')
215                 field.setAttribute("name", key)
216                 field.appendChild(doc.createTextNode(val))
217                 record.appendChild(field)
218
219         return record_list, noupdate
220
221     def _create_yaml_record(self, cr, uid, model, data, record_id):
222         record={'model': model, 'id': str(record_id)}
223         
224         model_pool = self.pool.get(model)
225         data_pool = self.pool.get('ir.model.data')
226         lids  = data_pool.search(cr, uid, [('model','=',model)])
227         
228         res = data_pool.read(cr, uid, lids[:1], ['module'])
229         attrs={}
230         if res:
231             self.depends[res[0]['module']]=True
232         fields = model_pool.fields_get(cr, uid)
233         defaults={}
234         try:
235             defaults[model] = model_pool.default_get(cr, uid, data)
236         except:
237             defaults[model]={}
238         for key,val in data.items():  
239             if ((key in defaults[model]) and (val ==  defaults[model][key])) and not(fields[key].get('required',False)):
240                 continue
241             if fields[key]['type'] in ('integer','float'):
242                 if not val:
243                     val=0.0
244                 attrs[key] = val
245             elif not (val or (fields[key]['type']=='function')):
246                 continue
247             elif fields[key]['type'] in ('boolean',):
248                 if not val:
249                     continue
250                 attrs[key] = val
251             elif fields[key]['type'] in ('many2one',):
252                 if type(val) in (type(''), type(u'')):
253                     id = val
254                 else:
255                     id, update = self._get_id(cr, uid, fields[key]['relation'], val)
256                 attrs[key] = str(id)
257             elif fields[key]['type'] in ('one2many',):
258                 items=[[]]
259                 for valitem in (val or []):
260                     if valitem[0] in (0,1):
261                         if key in model_pool._columns:
262                             fname = model_pool._columns[key]._fields_id
263                         else:
264                             fname = model_pool._inherit_fields[key][2]._fields_id
265                         del valitem[2][fname] #delete parent_field from child's fields list
266                         
267                         childrecord = self._create_yaml_record(cr, uid, fields[key]['relation'],valitem[2], None)
268                         items[0].append(childrecord['attrs'])
269                 attrs[key] = items
270             elif fields[key]['type'] in ('many2many',):
271                 if (key in defaults[model]) and (val[0][2] ==  defaults[model][key]):
272                     continue
273                 res = []
274                 for valitem in (val or []):
275                     if valitem[0]==6:
276                         for id2 in valitem[2]:
277                             id,update = self._get_id(cr, uid, fields[key]['relation'], id2)
278                             self.ids[(fields[key]['relation'],id2)] = id
279                             res.append(str(id))
280                         m2m=[res]
281                 if m2m[0]:
282                     attrs[key] = m2m
283             else:
284                 try:
285                     attrs[key]=str(val)
286                 except:
287                     attrs[key]=tools.ustr(val)
288                 attrs[key]=attrs[key].replace('"','\'')
289         record['attrs'] = attrs
290         return record
291
292     def get_copy_data(self, cr, uid, model, id, result):
293         res = []
294         obj=self.pool.get(model)
295         data=obj.read(cr, uid,[id])
296         if type(data)==type([]):
297             del data[0]['id']
298             data=data[0]
299         else:
300             del data['id']
301
302         mod_fields = obj.fields_get(cr, uid)
303         for f in filter(lambda a: isinstance(obj._columns[a], fields.function)\
304                     and (not obj._columns[a].store),obj._columns):
305             del data[f]
306             
307         for key,val in data.items():
308             if result.has_key(key):
309                 continue
310             if mod_fields[key]['type'] == 'many2one':
311                 if type(data[key])==type(True) or type(data[key])==type(1):
312                     result[key]=data[key]
313                 elif not data[key]:
314                     result[key] = False
315                 else:
316                     result[key]=data[key][0]
317
318             elif mod_fields[key]['type'] in ('one2many',):
319 #                continue # due to this start stop recording will not record one2many field
320                 rel = mod_fields[key]['relation']
321                 if len(data[key]):
322                     res1=[]
323                     for rel_id in data[key]:
324                         res=[0,0]
325                         res.append(self.get_copy_data(cr, uid,rel,rel_id,{}))
326                         res1.append(res)
327                     result[key]=res1
328                 else:
329                     result[key]=data[key]
330
331             elif mod_fields[key]['type'] == 'many2many':
332                 result[key]=[(6,0,data[key])]
333
334             else:
335                 result[key]=data[key]
336         for k,v in obj._inherits.items():
337             del result[v]
338         return result
339
340     def _create_function(self, cr, uid, doc, model, name, record_id):
341         record = doc.createElement('function')
342         record.setAttribute("name", name)
343         record.setAttribute("model", model)
344         record_list = [record]
345
346         value = doc.createElement('value')
347         value.setAttribute('eval', '[ref(\'%s\')]' % (record_id, ))
348         value.setAttribute('model', model)
349
350         record.appendChild(value)
351         return record_list, False
352
353     def _generate_object_xml(self, cr, uid, rec, recv, doc, result=None):
354         record_list = []
355         noupdate = False
356         if rec[3]=='write':
357             for id in rec[4]:
358                 id,update = self._get_id(cr, uid, rec[2], id)
359                 noupdate = noupdate or update
360                 if not id:
361                     continue
362                 record,update = self._create_record(cr, uid, doc, rec[2], rec[5], id)
363                 noupdate = noupdate or update
364                 record_list += record
365                 
366         elif rec[4] in ('menu_create',):
367             for id in rec[5]:
368                 id,update = self._get_id(cr, uid, rec[3], id)
369                 noupdate = noupdate or update
370                 if not id:
371                     continue
372                 record,update = self._create_function(cr, uid, doc, rec[3], rec[4], id)
373                 noupdate = noupdate or update
374                 record_list += record
375
376         elif rec[3]=='create':
377             id = self._create_id(cr, uid, rec[2],rec[4])
378             record,noupdate = self._create_record(cr, uid, doc, rec[2], rec[4], id)
379             self.ids[(rec[2], result)] = id
380             record_list += record
381
382         elif rec[3]=='copy':
383             data=self.get_copy_data(cr,uid,rec[2],rec[4],rec[5])
384             copy_rec=(rec[0],rec[1],rec[2],rec[3],rec[4],data,rec[5])
385             rec=copy_rec
386             rec_data=[(self.recording_data[0][0],rec,self.recording_data[0][2],self.recording_data[0][3])]
387             self.recording_data=rec_data
388             id = self._create_id(cr, uid, rec[2],rec[5])
389             record,noupdate = self._create_record(cr, uid, doc, rec[2], rec[5], id)
390             self.ids[(rec[2], result)] = id
391             record_list += record
392
393         return record_list,noupdate
394
395     def _generate_object_yaml(self, cr, uid, rec, result=None):
396         if self.mode=="create":
397             yml_id = self._create_id(cr, uid, rec[2],rec[4])
398             self.ids[(rec[2], result)] = yml_id
399             record = self._create_yaml_record(cr, uid, rec[2], rec[4], yml_id)
400             return record
401         if self.mode=="workflow":
402             id,update = self._get_id(cr, uid, rec[2], rec[4])
403             data = {}
404             data['model'] = rec[2]
405             data['action'] = rec[3]
406             data['ref'] = id
407             return data
408         if self.mode=="write":
409             id,update = self._get_id(cr, uid, rec[2],rec[4][0])
410             record = self._create_yaml_record(cr, uid, rec[2], rec[5], id)
411             return record
412         data=self.get_copy_data(cr,uid,rec[2],rec[4],rec[5])
413         copy_rec=(rec[0],rec[1],rec[2],rec[3],rec[4],data,rec[5])
414         rec=copy_rec
415         rec_data=[(self.recording_data[0][0],rec,self.recording_data[0][2],self.recording_data[0][3])]
416         self.recording_data=rec_data
417         id = self._create_id(cr, uid, rec[2],rec[5])
418         record = self._create_yaml_record(cr, uid, str(rec[2]), rec[5], id)
419         self.ids[(rec[2], result)] = id
420         return record
421
422     def _generate_function_yaml(self, cr, uid, args):
423         db, uid, model, action, ids, context = args
424         temp_context = context.copy()
425         active_id = temp_context['active_id']
426         active_model = temp_context['active_model']
427         active_id, update = self._get_id(cr, uid, active_model, active_id)
428         if not active_id:
429             active_id = 1
430         rec_id, noupdate = self._get_id(cr, uid, model, ids[0])
431         temp_context['active_id'] = "ref('%s')"%unicode(active_id)
432         temp_context['active_ids'][0] = "ref('%s')"%str(active_id)
433         function={}
434         function['model'] = model
435         function['action'] = action
436         attrs = "self.%s(cr, uid, [ref('%s')], {" %(action, rec_id, )
437         for k, v in temp_context.iteritems():
438             if isinstance(v, str):
439                 f= "'"+k+"': "+"'%s'"%v + ", "
440             else:
441                 v=str(v).replace('"', '')
442                 f= "'"+k+"': "+"%s"%v + ", "
443             attrs = attrs + f
444         attrs=str(attrs)+'})'
445         function['attrs'] = attrs
446         return function
447             
448     def _generate_assert_xml(self, rec, doc):
449         pass
450
451     def generate_xml(self, cr, uid):
452         # Create the minidom document
453         if len(self.recording_data):
454             self.ids = {}
455             doc = minidom.Document()
456             terp = doc.createElement("openerp")
457             doc.appendChild(terp)
458             for rec in self.recording_data:
459                 if rec[0]=='workflow':
460                     rec_id,noupdate = self._get_id(cr, uid, rec[1][2], rec[1][4])
461                     if not rec_id:
462                         continue
463                     data = doc.createElement("data")
464                     terp.appendChild(data)
465                     wkf = doc.createElement('workflow')
466                     data.appendChild(wkf)
467                     wkf.setAttribute("model", rec[1][2])
468                     wkf.setAttribute("action", rec[1][3])
469                     if noupdate:
470                         data.setAttribute("noupdate", "1")
471                     wkf.setAttribute("ref", rec_id)
472                 if rec[0]=='query':
473                     res_list,noupdate = self._generate_object_xml(cr, uid, rec[1], rec[2], doc, rec[3])
474                     data = doc.createElement("data")
475                     if noupdate:
476                         data.setAttribute("noupdate", "1")
477                     if res_list:
478                         terp.appendChild(data)
479                     for res in res_list:
480                         data.appendChild(res)
481                 elif rec[0]=='assert':
482                         pass
483             return doc.toprettyxml(indent="\t").encode('utf-8')
484
485     def generate_yaml(self, cr, uid):
486         self.ids = {}
487         if len(self.recording_data):
488             yaml_file='''\n'''
489     
490             for rec in self.recording_data:
491                 if rec[1][3] == 'create':
492                     self.mode="create"
493                 elif rec[1][3] == 'write':
494                     self.mode="write"
495                 elif rec[1][3] == 'copy':
496                     self.mode="copy"
497                 elif rec[0] == 'workflow':
498                     self.mode="workflow"
499                 elif rec[0] == 'osv_memory_action':
500                     self.mode='osv_memory_action'
501                 else:
502                     continue
503                 if self.mode == "workflow":
504                     record = self._generate_object_yaml(cr, uid, rec[1],rec[0])
505                     yaml_file += "!comment Performing a workflow action %s on module %s"%(record['action'], record['model']) + '''\n'''
506                     object = yaml.load(unicode('''\n !workflow %s \n'''%record,'iso-8859-1'))
507                     yaml_file += str(object) + '''\n\n'''
508                 elif self.mode == 'osv_memory_action':
509                     osv_action = self._generate_function_yaml(cr, uid, rec[1])
510                     yaml_file += "!comment Performing an osv_memory action %s on module %s"%(osv_action['action'], osv_action['model']) + '''\n'''
511                     osv_action = yaml.load(unicode('''\n !python %s \n'''%osv_action,'iso-8859-1'))
512                     yaml_file += str(osv_action) + '''\n'''
513                     attrs = yaml.dump(osv_action.attrs, default_flow_style=False)
514                     attrs = attrs.replace("''", '"')
515                     attrs = attrs.replace("'", '')
516                     yaml_file += attrs + '''\n\n'''
517                 else:
518                     record = self._generate_object_yaml(cr, uid, rec[1], rec[3])
519                     if self.mode == "create" or self.mode == "copy":
520                         yaml_file += "!comment Creating a %s record"%(record['model']) + '''\n'''
521                     else:
522                         yaml_file += "!comment Modifying a %s record"%(record['model']) + '''\n'''
523                     object = yaml.load(unicode('''\n !record %s \n'''%record,'iso-8859-1'))
524                     yaml_file += str(object) + '''\n'''
525                     attrs = yaml.dump(object.attrs, default_flow_style=False)
526                     yaml_file += attrs + '''\n\n'''
527                     
528         yaml_result=''''''
529         for line in yaml_file.split('\n'):
530             line=line.replace("''","'")
531             if (line.find('!record') == 0) or (line.find('!workflow') == 0) or (line.find('!python') == 0):
532                 line = "- \n" + "  " + line
533             elif line.find('!comment') == 0:
534                 line=line.replace('!comment','- \n ')   
535             elif line.find('- -') != -1:
536                 line=line.replace('- -','  -')
537                 line = "    " + line
538             else:
539                 line = "    " + line
540             yaml_result += line + '''\n'''
541             
542         return yaml_result
543
544 base_module_record()
545 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
546