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