f7ff899687543686898d6aa6a27d787fddf231a3
[odoo/odoo.git] / bin / report / custom.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 import os
24 import time
25 import netsvc
26
27 import tools
28 import print_xml
29 import render
30 from interface import report_int
31 import common
32 from osv.osv import except_osv
33 from osv.orm import browse_null
34 from osv.orm import browse_record_list
35 import pooler
36 from xml.dom import minidom
37 import libxml2
38 import libxslt
39 from pychart import *
40 import misc
41 import cStringIO
42
43 class external_pdf(render.render):
44     def __init__(self, pdf):
45         render.render.__init__(self)
46         self.pdf = pdf
47         self.output_type='pdf'
48     def _render(self):
49         return self.pdf
50
51 theme.use_color = 1
52
53
54 #TODO: devrait heriter de report_rml a la place de report_int 
55 # -> pourrait overrider que create_xml a la place de tout create
56 # heuu, ca marche pas ds tous les cas car graphs sont generes en pdf directment
57 # par pychart, et on passe donc pas par du rml
58 class report_custom(report_int):
59     def __init__(self, name):
60         report_int.__init__(self, name)
61     #
62     # PRE:
63     #    fields = [['address','city'],['name'], ['zip']]
64     #    conditions = [[('zip','==','3'),(,)],(,),(,)] #same structure as fields
65     #    row_canvas = ['Rue', None, None]
66     # POST:
67     #    [ ['ville','name','zip'] ]
68     #
69     def _row_get(self, cr, uid, objs, fields, conditions, row_canvas=None, group_by=None):
70         result = []
71         tmp = []
72         for obj in objs:
73             tobreak = False
74             for cond in conditions:
75                 if cond and cond[0]:
76                     c = cond[0]
77                     temp = c[0](eval('obj.'+c[1]))
78                     if not eval('\''+temp+'\''+' '+c[2]+' '+'\''+str(c[3])+'\''):
79                         tobreak = True
80             if tobreak:
81                 break
82             levels = {}
83             row = []
84             for i in range(len(fields)):
85                 if not fields[i]:
86                     row.append(row_canvas and row_canvas[i])
87                     if row_canvas[i]:
88                         row_canvas[i]=False
89                 elif len(fields[i])==1:
90                     if not isinstance(obj, browse_null):
91                         row.append(str(eval('obj.'+fields[i][0])))
92                     else:
93                         row.append(None)
94                 else:
95                     row.append(None)
96                     levels[fields[i][0]]=True
97             if not levels:
98                 result.append(row)
99             else:
100                 # Process group_by data first
101                 key = []
102                 if group_by != None and fields[group_by] != None:
103                     if fields[group_by][0] in levels.keys():
104                         key.append(fields[group_by][0])
105                     for l in levels.keys():
106                         if l != fields[group_by][0]:
107                             key.append(l)
108                 else:
109                     key = levels.keys()
110                 for l in key:
111                     objs = eval('obj.'+l)
112                     if not isinstance(objs, browse_record_list) and type(objs) <> type([]):
113                         objs = [objs]
114                     field_new = []
115                     cond_new = []
116                     for f in range(len(fields)):
117                         if (fields[f] and fields[f][0])==l:
118                             field_new.append(fields[f][1:])
119                             cond_new.append(conditions[f][1:])
120                         else:
121                             field_new.append(None)
122                             cond_new.append(None)
123                     if len(objs):
124                         result += self._row_get(cr, uid, objs, field_new, cond_new, row, group_by)
125                     else:
126                         result.append(row)
127         return result 
128
129
130     def create(self, cr, uid, ids, datas, context=None):
131         if not context:
132             context={}
133         self.pool = pooler.get_pool(cr.dbname)
134         report = self.pool.get('ir.report.custom').browse(cr, uid, [datas['report_id']])[0]
135         datas['model'] = report.model_id.model
136         if report.menu_id:
137             ids = self.pool.get(report.model_id.model).search(cr, uid, [])
138             datas['ids'] = ids
139
140         service = netsvc.LocalService("object_proxy")
141         report_id = datas['report_id']
142         report = service.execute(cr.dbname, uid, 'ir.report.custom', 'read', [report_id], context=context)[0]
143         fields = service.execute(cr.dbname, uid, 'ir.report.custom.fields', 'read', report['fields_child0'], context=context)
144
145         fields.sort(lambda x,y : x['sequence'] - y['sequence'])
146
147         if report['field_parent']:
148             parent_field = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [report['field_parent'][0]],['model'])
149         model_name = service.execute(cr.dbname, uid, 'ir.model', 'read', [report['model_id'][0]], ['model'],context=context)[0]['model']
150
151         fct = {}
152         fct['id'] = lambda x : x
153         fct['gety'] = lambda x: x.split('-')[0]
154         fct['in'] = lambda x: x.split(',')
155         new_fields = []
156         new_cond = []
157         for f in fields:
158             row = []
159             cond = []
160             for i in range(4):
161                 field_child = f['field_child'+str(i)]
162                 if field_child:
163                     row.append(
164                         service.execute(cr.dbname, uid, 
165                                         'ir.model.fields', 'read', [field_child[0]],
166                                         ['name'], context=context)[0]['name']
167                     )
168                     if f['fc'+str(i)+'_operande']:
169                         fct_name = 'id'
170                         cond_op =  f['fc'+str(i)+'_op']
171                         if len(f['fc'+str(i)+'_op'].split(',')) == 2:
172                             cond_op =  f['fc'+str(i)+'_op'].split(',')[1]
173                             fct_name = f['fc'+str(i)+'_op'].split(',')[0]
174                         cond.append((fct[fct_name], f['fc'+str(i)+'_operande'][1], cond_op, f['fc'+str(i)+'_condition']))
175                     else:
176                         cond.append(None)
177             new_fields.append(row)
178             new_cond.append(cond)
179         objs = self.pool.get(model_name).browse(cr, uid, ids)
180
181         # Group by
182         groupby = None
183         idx = 0
184         for f in fields:
185             if f['groupby']:
186                 groupby = idx
187             idx += 1
188
189
190         results = []
191         if report['field_parent']:
192             level = []
193             def build_tree(obj, level, depth):
194                 res = self._row_get(cr, uid,[obj], new_fields, new_cond)
195                 level.append(depth)
196                 new_obj = eval('obj.'+report['field_parent'][1])
197                 if not isinstance(new_obj, list) :
198                     new_obj = [new_obj]
199                 for o in  new_obj:
200                     if not isinstance(o, browse_null):
201                         res += build_tree(o, level, depth+1)
202                 return res
203
204             for obj in objs:
205                 results += build_tree(obj, level, 0)
206         else:
207             results = self._row_get(cr, uid,objs, new_fields, new_cond, group_by=groupby)
208
209         fct = {
210             'calc_sum': lambda l: reduce(lambda x,y: float(x)+float(y), filter(None, l), 0),
211             'calc_avg': lambda l: reduce(lambda x,y: float(x)+float(y), filter(None, l), 0) / (len(filter(None, l)) or 1.0),
212             'calc_max': lambda l: reduce(lambda x,y: max(x,y), [(i or 0.0) for i in l], 0),
213             'calc_min': lambda l: reduce(lambda x,y: min(x,y), [(i or 0.0) for i in l], 0),
214             'calc_count': lambda l: len(filter(None, l)),
215             'False': lambda l: '\r\n'.join(filter(None, l)),
216             'groupby': lambda l: reduce(lambda x,y: x or y, l)
217         }
218         new_res = []
219
220         prev = None
221         if groupby != None:
222             res_dic = {}
223             for line in results:
224                 if not line[groupby] and prev in res_dic:
225                     res_dic[prev].append(line)
226                 else:
227                     prev = line[groupby]
228                     if res_dic.has_key(line[groupby]):
229                         res_dic[line[groupby]].append(line)
230                     else:
231                         res_dic[line[groupby]] = []
232                         res_dic[line[groupby]].append(line)
233             #we use the keys in results since they are ordered, whereas in res_dic.heys() they aren't
234             for key in filter(None, [x[groupby] for x in results]):
235                 row = []
236                 for col in range(len(fields)):
237                     if col == groupby:
238                         row.append(fct['groupby'](map(lambda x: x[col], res_dic[key])))
239                     else:
240                         row.append(fct[str(fields[col]['operation'])](map(lambda x: x[col], res_dic[key])))
241                 new_res.append(row)
242             results = new_res
243         
244         if report['type']=='table':
245             if report['field_parent']:
246                 res = self._create_tree(uid, ids, report, fields, level, results, context)
247             else:
248                 sort_idx = 0
249                 for idx in range(len(fields)):
250                     if fields[idx]['name'] == report['sortby']:
251                         sort_idx = idx
252                         break
253                 try :
254                     results.sort(lambda x,y : cmp(float(x[sort_idx]),float(y[sort_idx])))
255                 except :
256                     results.sort(lambda x,y : cmp(x[sort_idx],y[sort_idx]))
257                 if report['limitt']:
258                     results = results[:int(report['limitt'])]
259                 res = self._create_table(uid, ids, report, fields, None, results, context)
260         elif report['type'] in ('pie','bar', 'line'):
261             results2 = []
262             prev = False
263             for r in results:
264                 row = []
265                 for j in range(len(r)):
266                     if j == 0 and not r[j]:
267                         row.append(prev)
268                     elif j == 0 and r[j]:
269                         prev = r[j]
270                         row.append(r[j])
271                     else:
272                         try:
273                             row.append(float(r[j]))
274                         except:
275                             row.append(r[j])
276                 results2.append(row)
277             if report['type']=='pie':
278                 res = self._create_pie(cr,uid, ids, report, fields, results2, context)
279             elif report['type']=='bar':
280                 res = self._create_bars(cr,uid, ids, report, fields, results2, context)
281             elif report['type']=='line':
282                 res = self._create_lines(cr,uid, ids, report, fields, results2, context)
283         return (self.obj.get(), 'pdf')
284
285     def _create_tree(self, uid, ids, report, fields, level, results, context):
286         pageSize=common.pageSize.get(report['print_format'], [210.0,297.0])
287         if report['print_orientation']=='landscape':
288             pageSize=[pageSize[1],pageSize[0]]
289
290         impl = minidom.getDOMImplementation()
291         new_doc = impl.createDocument(None, "report", None)
292         
293         # build header
294         config = new_doc.createElement("config")
295
296         def _append_node(name, text):
297             n = new_doc.createElement(name)
298             t = new_doc.createTextNode(text)
299             n.appendChild(t)
300             config.appendChild(n)
301
302         _append_node('date', time.strftime('%d/%m/%Y'))
303         _append_node('PageFormat', '%s' % report['print_format'])
304         _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize))
305         _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,))
306         _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,))
307
308         length = pageSize[0]-30-reduce(lambda x,y:x+(y['width'] or 0), fields, 0)
309         count = 0
310         for f in fields:
311             if not f['width']: count+=1
312         for f in fields:
313             if not f['width']:
314                 f['width']=round((float(length)/count)-0.5)
315
316         _append_node('tableSize', '%s' %  ','.join(map(lambda x: '%.2fmm' % (x['width'],), fields)))
317         _append_node('report-header', '%s' % (report['title'],))
318         _append_node('report-footer', '%s' % (report['footer'],))
319
320         new_doc.childNodes[0].appendChild(config)
321         header = new_doc.createElement("header")
322         
323         for f in fields:
324             field = new_doc.createElement("field")
325             field_txt = new_doc.createTextNode('%s' % (f['name'],))
326             field.appendChild(field_txt)
327             header.appendChild(field)
328         
329         new_doc.childNodes[0].appendChild(header)
330
331         lines = new_doc.createElement("lines")
332         level.reverse()
333         for line in results:
334             shift = level.pop()
335             node_line = new_doc.createElement("row")
336             prefix = '+'
337             for f in range(len(fields)):
338                 col = new_doc.createElement("col")
339                 if f == 0:
340                     col.setAttribute('para','yes')
341                     col.setAttribute('tree','yes')
342                     col.setAttribute('space',str(3*shift)+'mm')
343                 if line[f] != None:
344                     txt = new_doc.createTextNode(prefix+str(line[f]) or '')
345                 else:
346                     txt = new_doc.createTextNode('/')
347                 col.appendChild(txt)
348                 node_line.appendChild(col)
349                 prefix = ''
350             lines.appendChild(node_line)
351             
352         new_doc.childNodes[0].appendChild(lines)
353
354         styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl'))
355         style = libxslt.parseStylesheetDoc(styledoc)
356         doc = libxml2.parseDoc(new_doc.toxml())
357         rml_obj = style.applyStylesheet(doc, None)
358         rml = style.saveResultToString(rml_obj) 
359
360         self.obj = render.rml(rml)
361         self.obj.render()
362         return True
363
364
365     def _create_lines(self, cr, uid, ids, report, fields, results, context):
366         service = netsvc.LocalService("object_proxy")
367         pdf_string = cStringIO.StringIO()
368         can = canvas.init(fname=pdf_string, format='pdf')
369         
370         can.show(80,380,'/16/H'+report['title'])
371         
372         ar = area.T(size=(350,350),
373         #x_coord = category_coord.T(['2005-09-01','2005-10-22'],0),
374         x_axis = axis.X(label = fields[0]['name'], format="/a-30{}%s"),
375         y_axis = axis.Y(label = ', '.join(map(lambda x : x['name'], fields[1:]))))
376         
377         process_date = {}
378         process_date['D'] = lambda x : reduce(lambda xx,yy : xx+'-'+yy,x.split('-')[1:3])
379         process_date['M'] = lambda x : x.split('-')[1]
380         process_date['Y'] = lambda x : x.split('-')[0]
381
382         order_date = {}
383         order_date['D'] = lambda x : time.mktime((2005,int(x.split('-')[0]), int(x.split('-')[1]),0,0,0,0,0,0))
384         order_date['M'] = lambda x : x
385         order_date['Y'] = lambda x : x
386
387         abscissa = []
388         tmp = {}
389         
390         idx = 0 
391         date_idx = None
392         fct = {}
393         for f in fields:
394             field_id = (f['field_child3'] and f['field_child3'][0]) or (f['field_child2'] and f['field_child2'][0]) or (f['field_child1'] and f['field_child1'][0]) or (f['field_child0'] and f['field_child0'][0])
395             if field_id:
396                 type = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [field_id],['ttype'])
397                 if type[0]['ttype'] == 'date':
398                     date_idx = idx
399                     fct[idx] = process_date[report['frequency']] 
400                 else:
401                     fct[idx] = lambda x : x
402             else:
403                 fct[idx] = lambda x : x
404             idx+=1
405
406         # plots are usually displayed year by year
407         # so we do so if the first field is a date
408         data_by_year = {}
409         if date_idx != None:
410             for r in results:
411                 key = process_date['Y'](r[date_idx])
412                 if not data_by_year.has_key(key):
413                     data_by_year[key] = []
414                 for i in range(len(r)):
415                     r[i] = fct[i](r[i])
416                 data_by_year[key].append(r)
417         else:
418             data_by_year[''] = results
419
420         idx0 = 0
421         nb_bar = len(data_by_year)*(len(fields)-1)
422         colors = map(lambda x:line_style.T(color=x), misc.choice_colors(nb_bar))
423         abscissa = {}
424         for line in data_by_year.keys():
425             fields_bar = []
426             # sum data and save it in a list. An item for a fields
427             for d in data_by_year[line]:
428                 for idx in range(len(fields)-1):
429                     fields_bar.append({})
430                     if fields_bar[idx].has_key(d[0]):
431                         fields_bar[idx][d[0]] += d[idx+1]
432                     else:
433                         fields_bar[idx][d[0]] = d[idx+1]
434             for idx  in range(len(fields)-1):
435                 data = {}
436                 for k in fields_bar[idx].keys():
437                     if data.has_key(k):
438                         data[k] += fields_bar[idx][k]
439                     else:
440                         data[k] = fields_bar[idx][k]
441                 data_cum = []
442                 prev = 0.0
443                 keys = data.keys()
444                 keys.sort()
445                 # cumulate if necessary
446                 for k in keys:
447                     data_cum.append([k, float(data[k])+float(prev)])
448                     if fields[idx+1]['cumulate']:
449                         prev += data[k]
450                 idx0 = 0
451                 plot = line_plot.T(label=fields[idx+1]['name']+' '+str(line), data = data_cum, line_style=colors[idx0*(len(fields)-1)+idx])
452                 ar.add_plot(plot)
453                 abscissa.update(fields_bar[idx])
454                 idx0 += 1
455         
456         abscissa = map(lambda x : [x, None], abscissa)
457         ar.x_coord = category_coord.T(abscissa,0)
458         ar.draw(can)
459
460         can.close()
461         self.obj = external_pdf(pdf_string.getvalue())
462         self.obj.render()
463         pdf_string.close()
464         return True
465
466
467
468     def _create_bars(self, cr, uid, ids, report, fields, results, context):
469         service = netsvc.LocalService("object_proxy")
470         pdf_string = cStringIO.StringIO()
471         can = canvas.init(fname=pdf_string, format='pdf')
472         
473         can.show(80,380,'/16/H'+report['title'])
474         
475         process_date = {}
476         process_date['D'] = lambda x : reduce(lambda xx,yy : xx+'-'+yy,x.split('-')[1:3])
477         process_date['M'] = lambda x : x.split('-')[1]
478         process_date['Y'] = lambda x : x.split('-')[0]
479
480         order_date = {}
481         order_date['D'] = lambda x : time.mktime((2005,int(x.split('-')[0]), int(x.split('-')[1]),0,0,0,0,0,0))
482         order_date['M'] = lambda x : x
483         order_date['Y'] = lambda x : x
484
485         ar = area.T(size=(350,350),
486             x_axis = axis.X(label = fields[0]['name'], format="/a-30{}%s"),
487             y_axis = axis.Y(label = ', '.join(map(lambda x : x['name'], fields[1:]))))
488
489         idx = 0 
490         date_idx = None
491         fct = {}
492         for f in fields:
493             field_id = (f['field_child3'] and f['field_child3'][0]) or (f['field_child2'] and f['field_child2'][0]) or (f['field_child1'] and f['field_child1'][0]) or (f['field_child0'] and f['field_child0'][0])
494             if field_id:
495                 type = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [field_id],['ttype'])
496                 if type[0]['ttype'] == 'date':
497                     date_idx = idx
498                     fct[idx] = process_date[report['frequency']] 
499                 else:
500                     fct[idx] = lambda x : x
501             else:
502                 fct[idx] = lambda x : x
503             idx+=1
504         
505         # plot are usually displayed year by year
506         # so we do so if the first field is a date
507         data_by_year = {}
508         if date_idx != None:
509             for r in results:
510                 key = process_date['Y'](r[date_idx])
511                 if not data_by_year.has_key(key):
512                     data_by_year[key] = []
513                 for i in range(len(r)):
514                     r[i] = fct[i](r[i])
515                 data_by_year[key].append(r)
516         else:
517             data_by_year[''] = results
518
519
520         nb_bar = len(data_by_year)*(len(fields)-1)
521         colors = map(lambda x:fill_style.Plain(bgcolor=x), misc.choice_colors(nb_bar))
522         
523         abscissa = {}
524         for line in data_by_year.keys():
525             fields_bar = []
526             # sum data and save it in a list. An item for a fields
527             for d in data_by_year[line]:
528                 for idx in range(len(fields)-1):
529                     fields_bar.append({})
530                     if fields_bar[idx].has_key(d[0]):
531                         fields_bar[idx][d[0]] += d[idx+1]
532                     else:
533                         fields_bar[idx][d[0]] = d[idx+1]
534             for idx  in range(len(fields)-1):
535                 data = {}
536                 for k in fields_bar[idx].keys():
537                     if data.has_key(k):
538                         data[k] += fields_bar[idx][k]
539                     else:
540                         data[k] = fields_bar[idx][k]
541                 data_cum = []
542                 prev = 0.0
543                 keys = data.keys()
544                 keys.sort()
545                 # cumulate if necessary
546                 for k in keys:
547                     data_cum.append([k, float(data[k])+float(prev)])
548                     if fields[idx+1]['cumulate']:
549                         prev += data[k]
550                         
551                 idx0 = 0
552                 plot = bar_plot.T(label=fields[idx+1]['name']+' '+str(line), data = data_cum, cluster=(idx0*(len(fields)-1)+idx,nb_bar), fill_style=colors[idx0*(len(fields)-1)+idx])
553                 ar.add_plot(plot)
554                 abscissa.update(fields_bar[idx])
555             idx0 += 1
556         abscissa = map(lambda x : [x, None], abscissa)
557         ar.x_coord = category_coord.T(abscissa,0)
558         ar.draw(can)
559
560         can.close()
561         self.obj = external_pdf(pdf_string.getvalue())
562         self.obj.render()
563         pdf_string.close()
564         return True
565
566     def _create_pie(self, cr, uid, ids, report, fields, results, context):
567         pdf_string = cStringIO.StringIO()
568         can = canvas.init(fname=pdf_string, format='pdf')
569         ar = area.T(size=(350,350), legend=legend.T(),
570                     x_grid_style = None, y_grid_style = None)
571         colors = map(lambda x:fill_style.Plain(bgcolor=x), misc.choice_colors(len(results)))
572
573         if reduce(lambda x,y : x+y, map(lambda x : x[1],results)) == 0.0:
574             raise except_osv(_('Error'), _("The sum of the data (2nd field) is null.\nWe can't draw a pie chart !"))
575
576         plot = pie_plot.T(data=results, arc_offsets=[0,10,0,10],
577                           shadow = (2, -2, fill_style.gray50),
578                           label_offset = 25,
579                           arrow_style = arrow.a3,
580                           fill_styles=colors)
581         ar.add_plot(plot)
582         ar.draw(can)
583         can.close()
584         self.obj = external_pdf(pdf_string.getvalue())
585         self.obj.render()
586         pdf_string.close()
587         return True
588
589     def _create_table(self, uid, ids, report, fields, tree, results, context):
590         pageSize=common.pageSize.get(report['print_format'], [210.0,297.0])
591         if report['print_orientation']=='landscape':
592             pageSize=[pageSize[1],pageSize[0]]
593
594         impl = minidom.getDOMImplementation()
595         new_doc = impl.createDocument(None, "report", None)
596         
597         # build header
598         config = new_doc.createElement("config")
599
600         def _append_node(name, text):
601             n = new_doc.createElement(name)
602             t = new_doc.createTextNode(text)
603             n.appendChild(t)
604             config.appendChild(n)
605
606         _append_node('date', time.strftime('%d/%m/%Y'))
607         _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize))
608         _append_node('PageFormat', '%s' % report['print_format'])
609         _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,))
610         _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,))
611
612         length = pageSize[0]-30-reduce(lambda x,y:x+(y['width'] or 0), fields, 0)
613         count = 0
614         for f in fields:
615             if not f['width']: count+=1
616         for f in fields:
617             if not f['width']:
618                 f['width']=round((float(length)/count)-0.5)
619
620         _append_node('tableSize', '%s' %  ','.join(map(lambda x: '%.2fmm' % (x['width'],), fields)))
621         _append_node('report-header', '%s' % (report['title'],))
622         _append_node('report-footer', '%s' % (report['footer'],))
623
624         new_doc.childNodes[0].appendChild(config)
625         header = new_doc.createElement("header")
626         
627         for f in fields:
628             field = new_doc.createElement("field")
629             field_txt = new_doc.createTextNode('%s' % (f['name'],))
630             field.appendChild(field_txt)
631             header.appendChild(field)
632         
633         new_doc.childNodes[0].appendChild(header)
634
635         lines = new_doc.createElement("lines")
636         for line in results:
637             node_line = new_doc.createElement("row")
638             for f in range(len(fields)):
639                 col = new_doc.createElement("col")
640                 col.setAttribute('tree','no')
641                 if line[f] != None:
642                     txt = new_doc.createTextNode(str(line[f] or ''))
643                 else:
644                     txt = new_doc.createTextNode('/')
645                 col.appendChild(txt)
646                 node_line.appendChild(col)
647             lines.appendChild(node_line)    
648             
649         new_doc.childNodes[0].appendChild(lines)
650
651 #       file('/tmp/terp.xml','w+').write(new_doc.toxml())
652
653         styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl'))
654         style = libxslt.parseStylesheetDoc(styledoc)
655         doc = libxml2.parseDoc(new_doc.toxml())
656         rml_obj = style.applyStylesheet(doc, None)
657         rml = style.saveResultToString(rml_obj) 
658
659         self.obj = render.rml(rml)
660         self.obj.render()
661         return True
662 report_custom('report.custom')
663
664
665 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
666