return blank for non float parameters
[odoo/odoo.git] / bin / report / report_sxw.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 from interface import  report_rml
24 import StringIO
25 import base64
26 import copy
27 import ir
28 import locale
29 import mx.DateTime
30 import netsvc
31 import os
32 import osv
33 import pooler
34 import re
35 import time
36 import tools
37 import warnings
38 import xml.dom.minidom
39 import zipfile
40
41 DT_FORMAT = '%Y-%m-%d'
42 DHM_FORMAT = '%Y-%m-%d %H:%M:%S'
43 HM_FORMAT = '%H:%M:%S'
44
45 if not hasattr(locale, 'nl_langinfo'):
46     locale.nl_langinfo = lambda *a: '%x'
47
48 if not hasattr(locale, 'D_FMT'):
49     locale.D_FMT = None
50
51 rml_parents = {
52     'tr':1,
53     'li':1,
54     'story': 0,
55     'section': 0
56 }
57
58 rml_tag="para"
59
60 sxw_parents = {
61     'table-row': 1,
62     'list-item': 1,
63     'body': 0,
64     'section': 0,
65 }
66
67 sxw_tag = "p"
68
69 rml2sxw = {
70     'para': 'p',
71 }
72
73 _LOCALE2WIN32 = {
74     'af_ZA': 'Afrikaans_South Africa',
75     'sq_AL': 'Albanian_Albania',
76     'ar_SA': 'Arabic_Saudi Arabia',
77     'eu_ES': 'Basque_Spain',
78     'be_BY': 'Belarusian_Belarus',
79     'bs_BA': 'Serbian (Latin)',
80     'bg_BG': 'Bulgarian_Bulgaria',
81     'ca_ES': 'Catalan_Spain',
82     'hr_HR': 'Croatian_Croatia',
83     'zh_CN': 'Chinese_China',
84     'zh_TW': 'Chinese_Taiwan',
85     'cs_CZ': 'Czech_Czech Republic',
86     'da_DK': 'Danish_Denmark',
87     'nl_NL': 'Dutch_Netherlands',
88     'et_EE': 'Estonian_Estonia',
89     'fa_IR': 'Farsi_Iran',
90     'ph_PH': 'Filipino_Philippines',
91     'fi_FI': 'Finnish_Finland',
92     'fr_FR': 'French_France',
93     'fr_BE': 'French_France',
94     'fr_CH': 'French_France',
95     'fr_CA': 'French_France',
96     'ga': 'Scottish Gaelic',
97     'gl_ES': 'Galician_Spain',
98     'ka_GE': 'Georgian_Georgia',
99     'de_DE': 'German_Germany',
100     'el_GR': 'Greek_Greece',
101     'gu': 'Gujarati_India',
102     'he_IL': 'Hebrew_Israel',
103     'hi_IN': 'Hindi',
104     'hu': 'Hungarian_Hungary',
105     'is_IS': 'Icelandic_Iceland',
106     'id_ID': 'Indonesian_indonesia',
107     'it_IT': 'Italian_Italy',
108     'ja_JP': 'Japanese_Japan',
109     'kn_IN': 'Kannada',
110     'km_KH': 'Khmer',
111     'ko_KR': 'Korean_Korea',
112     'lo_LA': 'Lao_Laos',
113     'lt_LT': 'Lithuanian_Lithuania',
114     'lat': 'Latvian_Latvia',
115     'ml_IN': 'Malayalam_India',
116     'id_ID': 'Indonesian_indonesia',
117     'mi_NZ': 'Maori',
118     'mn': 'Cyrillic_Mongolian',
119     'no_NO': 'Norwegian_Norway',
120     'nn_NO': 'Norwegian-Nynorsk_Norway',
121     'pl': 'Polish_Poland',
122     'pt_PT': 'Portuguese_Portugal',
123     'pt_BR': 'Portuguese_Brazil',
124     'ro_RO': 'Romanian_Romania',
125     'ru_RU': 'Russian_Russia',
126     'mi_NZ': 'Maori',
127     'sr_CS': 'Serbian (Cyrillic)_Serbia and Montenegro',
128     'sk_SK': 'Slovak_Slovakia',
129     'sl_SI': 'Slovenian_Slovenia',
130     'es_ES': 'Spanish_Spain',
131     'sv_SE': 'Swedish_Sweden',
132     'ta_IN': 'English_Australia',
133     'th_TH': 'Thai_Thailand',
134     'mi_NZ': 'Maori',
135     'tr_TR': 'Turkish_Turkey',
136     'uk_UA': 'Ukrainian_Ukraine',
137     'vi_VN': 'Vietnamese_Viet Nam',
138 }
139
140 class _format(object):
141     def set_value(self, name, object, field):
142         #super(_date_format, self).__init__(self)
143         self.object = object
144         self._field = field
145         self.name=name
146         lc, encoding = locale.getdefaultlocale()
147         if not encoding:
148             encoding = 'UTF-8'
149         if encoding == 'utf':
150             encoding = 'UTF-8'
151         if encoding == 'cp1252':
152             encoding= '1252'
153         lang = self.object._context.get('lang', 'en_US') or 'en_US'
154         try:
155             if os.name == 'nt':
156                 locale.setlocale(locale.LC_ALL, _LOCALE2WIN32.get(lang, lang) + '.' + encoding)
157             else:
158                 locale.setlocale(locale.LC_ALL,str( lang + '.' + encoding))
159         except Exception:
160             netsvc.Logger().notifyChannel('report', netsvc.LOG_WARNING,
161                     'report %s: unable to set locale "%s"' % (self.name,
162                         self.object._context.get('lang', 'en_US') or 'en_US'))
163
164
165 class _float_format(float, _format):
166     def __str__(self):
167         if not self.object._context:
168             return locale.format('%f', self.name, True)
169         digit = 2
170         if hasattr(self._field, 'digits') and self._field.digits:
171             digit = self._field.digits[1]
172         return locale.format('%.' + str(digit) + 'f', self.name, True)
173
174
175 class _int_format(int, _format):
176     def __str__(self):
177         return locale.format('%d', self.name, True)
178
179
180 class _date_format(str, _format):
181     def __str__(self):
182         if not self.object._context:
183             return self.name
184
185         if self.name:
186             try :
187                 datedata = time.strptime(self.name, DT_FORMAT)
188                 return time.strftime(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y'),
189                     datedata)
190             except :
191                 pass
192         return ''
193
194
195 _fields_process = {
196     'float': _float_format,
197     'date': _date_format,
198     'integer': _int_format
199 }
200
201 #
202 # Context: {'node': node.dom}
203 #
204 class browse_record_list(list):
205     def __init__(self, lst, context):
206         super(browse_record_list, self).__init__(lst)
207         self.context = context
208
209     def __getattr__(self, name):
210         res = browse_record_list([getattr(x,name) for x in self], self.context)
211         return res
212
213     def __str__(self):
214         return "browse_record_list("+str(len(self))+")"
215
216     def repeatIn(self, name):
217         warnings.warn('Use repeatIn(object_list, \'variable\')', DeprecationWarning)
218         node = self.context['_node']
219         parents = self.context.get('parents', rml_parents)
220         node.data = ''
221         while True:
222             if not node.parentNode:
223                 break
224             node = node.parentNode
225             if node.nodeType == node.ELEMENT_NODE and node.localName in parents:
226                 break
227         parent_node = node
228         if not len(self):
229             return None
230         nodes = [(0,node)]
231         for i in range(1,len(self)):
232             newnode = parent_node.cloneNode(1)
233             n = parent_node.parentNode
234             n.insertBefore(newnode, parent_node)
235             nodes.append((i,newnode))
236         for i,node in nodes:
237             self.context[name] = self[i]
238             self.context['_self']._parse_node(node)
239         return None
240
241 class rml_parse(object):
242     def __init__(self, cr, uid, name, parents=rml_parents, tag=rml_tag, context=None):
243         if not context:
244             context={}
245         self.cr = cr
246         self.uid = uid
247         self.pool = pooler.get_pool(cr.dbname)
248         user = self.pool.get('res.users').browse(cr, uid, uid, fields_process=_fields_process)
249         self.localcontext = {
250             'user': user,
251             'company': user.company_id,
252             'repeatIn': self.repeatIn,
253             'setLang': self.setLang,
254             'setTag': self.setTag,
255             'removeParentNode': self.removeParentNode,
256             'format': self.format,
257             'formatLang': self.formatLang,
258             'logo' : user.company_id.logo,
259             'lang' : user.company_id.partner_id.lang,
260         }
261         self.localcontext.update(context)
262         self.rml_header = user.company_id.rml_header
263         self.rml_header2 = user.company_id.rml_header2
264         self.logo = user.company_id.logo
265         self.name = name
266         self._regex = re.compile('\[\[(.+?)\]\]')
267         self._transl_regex = re.compile('(\[\[.+?\]\])')
268         self._node = None
269         self.parents = parents
270         self.tag = tag
271         self._lang_cache = {}
272 #       self.already = {}
273
274     def setTag(self, oldtag, newtag, attrs=None):
275         if not attrs:
276             attrs={}
277         node = self._find_parent(self._node, [oldtag])
278         if node:
279             node.tagName = newtag
280             for key, val in attrs.items():
281                 node.setAttribute(key, val)
282         return None
283
284     def format(self, text, oldtag=None):
285         if not oldtag:
286             oldtag = self.tag
287         self._node.data = ''
288         node = self._find_parent(self._node, [oldtag])
289         ns = None
290         if node:
291             pp = node.parentNode
292             ns = node.nextSibling
293             pp.removeChild(node)
294             self._node = pp
295             
296         lst = tools.ustr(text).split('\n')
297         if not (text and lst):
298             return None
299         nodes = []
300         for i in range(len(lst)):
301             newnode = node.cloneNode(1)
302             newnode.tagName=rml_tag
303             newnode.childNodes[0].data = lst[i]
304             if ns:
305                 pp.insertBefore(newnode, ns)
306             else:
307                 pp.appendChild(newnode)
308             nodes.append((i, newnode))
309
310     def removeParentNode(self, tag=None):
311         if not tag:
312             tag = self.tag
313         if self.tag == sxw_tag and rml2sxw.get(tag, False):
314             tag = rml2sxw[tag]
315         node = self._find_parent(self._node, [tag])
316         if node:
317             parentNode = node.parentNode
318             parentNode.removeChild(node)
319             self._node = parentNode
320
321     def setLang(self, lang):
322         self.localcontext['lang'] = lang
323         for obj in self.objects:
324             obj._context['lang'] = lang
325             for table in obj._cache:
326                 for id in obj._cache[table]:
327                     self._lang_cache.setdefault(obj._context['lang'], {}).setdefault(table,
328                             {}).update(obj._cache[table][id])
329                     if lang in self._lang_cache \
330                             and table in self._lang_cache[lang] \
331                             and id in self._lang_cache[lang][table]:
332                         obj._cache[table][id] = self._lang_cache[lang][table][id]
333                     else:
334                         obj._cache[table][id] = {'id': id}
335
336
337     def formatLang(self, value, digits=2, date=False,date_time=False, grouping=True, monetary=False, currency=None):
338         if (not isinstance(value,float)) and (not value):
339             return ''
340         pool_lang=self.pool.get('res.lang')
341         lang = self.localcontext.get('lang', 'en_US') or 'en_US'
342         lang_obj = pool_lang.browse(self.cr,self.uid,pool_lang.search(self.cr,self.uid,[('code','=',lang)])[0])
343         if date or date_time:
344             date_format = lang_obj.date_format
345             if date_time:
346                 date_format = lang_obj.date_format + " " + lang_obj.time_format
347             if not isinstance(value, time.struct_time):
348                 # assume string, parse it
349                 if len(str(value)) == 10:
350                     # length of date like 2001-01-01 is ten
351                     # assume format '%Y-%m-%d'
352                     date = mx.DateTime.strptime(value,DT_FORMAT)
353                 else:
354                     # assume format '%Y-%m-%d %H:%M:%S'
355                     value = str(value)[:19]
356                     date = mx.DateTime.strptime(str(value),DHM_FORMAT)
357             else:
358                 date = mx.DateTime.DateTime(*(value.timetuple()[:6]))
359             return date.strftime(date_format)
360         return lang_obj.format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary)
361     
362 #    def formatLang(self, value, digit=2, date=False):
363 #        if not value:
364 #            return ''
365 #        lc, encoding = locale.getdefaultlocale()
366 #        if not encoding:
367 #            encoding = 'UTF-8'
368 #        if encoding == 'utf':
369 #            encoding = 'UTF-8'
370 #        if encoding == 'cp1252':
371 #            encoding= '1252'
372 #        lang = self.localcontext.get('lang', 'en_US') or 'en_US'
373 #        try:
374 #            if os.name == 'nt':
375 #                locale.setlocale(locale.LC_ALL, _LOCALE2WIN32.get(lang, lang) + '.' + encoding)
376 #            else:
377 #                locale.setlocale(locale.LC_ALL, lang + '.' + encoding)
378 #        except Exception:
379 #            netsvc.Logger().notifyChannel('report', netsvc.LOG_WARNING,
380 #                    'report %s: unable to set locale "%s"' % (self.name,
381 #                        self.localcontext.get('lang', 'en_US') or 'en_US'))
382 #        if date:
383 #            date = time.strptime(value, DT_FORMAT)
384 #            return time.strftime(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y'),
385 #                    date)
386 #        return locale.format('%.' + str(digit) + 'f', value, True)
387
388     def repeatIn(self, lst, name, nodes_parent=False):
389         self._node.data = ''
390         node = self._find_parent(self._node, nodes_parent or self.parents)
391
392         pp = node.parentNode
393         ns = node.nextSibling
394         pp.removeChild(node)
395         self._node = pp
396
397         if not len(lst):
398             return None
399         nodes = []
400         for i in range(len(lst)):
401             newnode = node.cloneNode(1)
402             if ns:
403                 pp.insertBefore(newnode, ns)
404             else:
405                 pp.appendChild(newnode)
406             nodes.append((i, newnode))
407         for i, node in nodes:
408             self.node_context[node] = {name: lst[i]}
409         return None
410
411     def _eval(self, expr):
412         try:
413             res = eval(expr, self.localcontext)
414             if (res is None) or (res=='') or (res is False):
415                 res = ''
416         except Exception,e:
417             import traceback, sys
418             tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))
419             netsvc.Logger().notifyChannel('report', netsvc.LOG_ERROR,
420                     'report %s:\n%s\n%s\nexpr: %s' % (self.name, tb_s, str(e),
421                         expr.encode('utf-8')))
422             res = ''
423         return res
424
425     def _find_parent(self, node, parents):
426         while True:
427             if not node.parentNode:
428                 return False
429             node = node.parentNode
430             if node.nodeType == node.ELEMENT_NODE and node.localName in parents:
431                 break
432         return node
433
434     def _parse_text(self, text, level=None):
435         if not level:
436             level=[]
437         res = self._regex.findall(text)
438         todo = []
439         # translate the text
440         # the "split [[]] if not match [[]]" is not very nice, but I
441         # don't see how I could do it better...
442         # what I'd like to do is a re.sub(NOT pattern, func, string)
443         # but I don't know how to do that...
444         # translate the RML file
445         if 'lang' in self.localcontext:
446             lang = self.localcontext['lang']
447             if lang and text and not text.isspace():
448                 transl_obj = self.pool.get('ir.translation')
449                 piece_list = self._transl_regex.split(text)
450                 for pn in range(len(piece_list)):
451                     if not self._transl_regex.match(piece_list[pn]):
452                         source_string = piece_list[pn].replace('\n', ' ').strip()
453                         if len(source_string):
454                             translated_string = transl_obj._get_source(self.cr, self.uid, self.name, 'rml', lang, source_string)
455                             if translated_string:
456                                 piece_list[pn] = piece_list[pn].replace(source_string, translated_string)
457                 text = ''.join(piece_list)
458         for key in res:
459             newtext = self._eval(key)
460             for i in range(len(level)):
461                 if isinstance(newtext, list):
462                     newtext = newtext[level[i]]
463             if isinstance(newtext, list):
464                 todo.append((key, newtext))
465             else:
466                 # if there are two [[]] blocks the same, it will replace both
467                 # but it's ok because it should evaluate to the same thing
468                 # anyway
469                 newtext = tools.ustr(newtext)
470                 text = text.replace('[['+key+']]', newtext)
471         self._node.data = text
472         if len(todo):
473             for key, newtext in todo:
474                 parent_node = self._find_parent(self._node, parents)
475                 assert parents.get(parent_node.localName, False), 'No parent node found !'
476                 nodes = [parent_node]
477                 for i in range(len(newtext) - 1):
478                     newnode = parent_node.cloneNode(1)
479                     if parents.get(parent_node.localName, False):
480                         n = parent_node.parentNode
481                         parent_node.parentNode.insertAfter(newnode, parent_node)
482                         nodes.append(newnode)
483             return False
484         return text
485
486     def _parse_node(self):
487         level = []
488         while True:
489             if self._node.nodeType==self._node.ELEMENT_NODE:
490                 if self._node.hasAttribute('expr'):
491                     newattrs = self._eval(self._node.getAttribute('expr'))
492                     for key,val in newattrs.items():
493                         self._node.setAttribute(key,val)
494
495             if self._node.hasChildNodes():
496                 self._node = self._node.firstChild
497             elif self._node.nextSibling:
498                 self._node = self._node.nextSibling
499             else:
500                 while self._node and not self._node.nextSibling:
501                     self._node = self._node.parentNode
502                 if not self._node:
503                     break
504                 self._node = self._node.nextSibling
505             if self._node in self.node_context:
506                 self.localcontext.update(self.node_context[self._node])
507             if self._node.nodeType in (self._node.CDATA_SECTION_NODE, self._node.TEXT_NODE):
508 #               if self._node in self.already:
509 #                   self.already[self._node] += 1
510 #                   print "second pass!", self.already[self._node], '---%s---' % self._node.data
511 #               else:
512 #                   self.already[self._node] = 0
513                 self._parse_text(self._node.data, level)
514         return True
515
516     def _find_node(self, node, localname):
517         if node.localName==localname:
518             return node
519         for tag in node.childNodes:
520             if tag.nodeType==tag.ELEMENT_NODE:
521                 found = self._find_node(tag, localname)
522                 if found:
523                     return found
524         return False
525
526     def _add_header(self, node, header=1):
527         if header==2:
528             rml_head =  self.rml_header2
529         else:
530             rml_head =  self.rml_header
531
532         # Refactor this patch, to use the minidom interface
533         if self.logo and (rml_head.find('company.logo')<0 or rml_head.find('<image')<0) and rml_head.find('<!--image')<0:
534             rml_head =  rml_head.replace('<pageGraphics>','''<pageGraphics> <image x="10" y="26cm" height="70" width="90" >[[company.logo]] </image> ''')
535         if not self.logo and rml_head.find('company.logo')>=0:
536             rml_head = rml_head.replace('<image','<!--image')
537             rml_head = rml_head.replace('</image>','</image-->')
538
539         head_dom = xml.dom.minidom.parseString(rml_head)
540         #for frame in head_dom.getElementsByTagName('frame'):
541         #   frame.parentNode.removeChild(frame)
542         node2 = head_dom.documentElement
543         for tag in node2.childNodes:
544             if tag.nodeType==tag.ELEMENT_NODE:
545                 found = self._find_node(node, tag.localName)
546         #       rml_frames = found.getElementsByTagName('frame')
547                 if found:
548                     if tag.hasAttribute('position') and (tag.getAttribute('position')=='inside'):
549                         found.appendChild(tag)
550                     else:
551                         found.parentNode.replaceChild(tag, found)
552         #       for frame in rml_frames:
553         #           tag.appendChild(frame)
554         return True
555
556     def preprocess(self, objects, data, ids):
557         self.localcontext['data'] = data
558         self.localcontext['objects'] = objects
559         self.datas = data
560         self.ids = ids
561         self.objects = objects
562
563     def _parse(self, rml_dom, objects, data, header=0):
564         self.node_context = {}
565         self.dom = rml_dom
566         self._node = self.dom.documentElement
567         if header:
568             self._add_header(self._node, header)
569         self._parse_node()
570         res = self.dom.documentElement.toxml('utf-8')
571         return res
572
573 class report_sxw(report_rml):
574     def __init__(self, name, table, rml, parser=rml_parse, header=True, store=False):
575         report_rml.__init__(self, name, table, rml, '')
576         self.name = name
577         self.parser = parser
578         self.header = header
579         self.store = store
580
581     def getObjects(self, cr, uid, ids, context):
582         table_obj = pooler.get_pool(cr.dbname).get(self.table)
583         return table_obj.browse(cr, uid, ids, list_class=browse_record_list, context=context,
584             fields_process=_fields_process)
585
586     def create(self, cr, uid, ids, data, context=None):
587         if not context:
588             context={}
589         pool = pooler.get_pool(cr.dbname)
590         ir_obj = pool.get('ir.actions.report.xml')
591         report_xml_ids = ir_obj.search(cr, uid,
592                 [('report_name', '=', self.name[7:])], context=context)
593
594         if report_xml_ids:
595             report_xml = ir_obj.browse(cr, uid, report_xml_ids[0],
596                     context=context)
597             attach = report_xml.attachment
598         else:
599             ir_menu_report_obj = pool.get('ir.ui.menu')
600             report_menu_ids = ir_menu_report_obj.search(cr, uid,
601                     [('id', 'in', ids)], context=context)
602             title = ''
603             if report_menu_ids:
604                 report_name = ir_menu_report_obj.browse(cr, uid, report_menu_ids[0],
605                     context=context)
606                 title = report_name.name
607             rml = tools.file_open(self.tmpl, subdir=None).read()
608             report_type= data.get('report_type', 'pdf')
609             class a(object):
610                 def __init__(self, *args, **argv):
611                     for key,arg in argv.items():
612                         setattr(self, key, arg)
613             report_xml = a(title=title, report_type=report_type, report_rml_content=rml, name=title, attachment=False, header=self.header)
614             attach = False
615
616         if attach:
617             objs = self.getObjects(cr, uid, ids, context)
618             results = []
619             for obj in objs:
620                 aname = eval(attach, {'object':obj, 'time':time})
621                 result = False
622                 if report_xml.attachment_use and aname and context.get('attachment_use', True):
623                     aids = pool.get('ir.attachment').search(cr, uid, [('datas_fname','=',aname+'.pdf'),('res_model','=',self.table),('res_id','=',obj.id)])
624                     if aids:
625                         d = base64.decodestring(pool.get('ir.attachment').browse(cr, uid, aids[0]).datas)
626                         results.append((d,'pdf'))
627                         continue
628
629                 result = self.create_single(cr, uid, [obj.id], data, report_xml, context)
630                 if aname:
631                     name = aname+'.'+result[1]
632                     pool.get('ir.attachment').create(cr, uid, {
633                         'name': aname,
634                         'datas': base64.encodestring(result[0]),
635                         'datas_fname': name,
636                         'res_model': self.table,
637                         'res_id': obj.id,
638                         }, context=context
639                     )
640                     cr.commit()
641                 results.append(result)
642
643             if results[0][1]=='pdf':
644                 from pyPdf import PdfFileWriter, PdfFileReader
645                 import cStringIO
646                 output = PdfFileWriter()
647                 for r in results:
648                     reader = PdfFileReader(cStringIO.StringIO(r[0]))
649                     for page in range(reader.getNumPages()):
650                         output.addPage(reader.getPage(page))
651                 s = cStringIO.StringIO()
652                 output.write(s)
653                 return s.getvalue(), results[0][1]
654         return self.create_single(cr, uid, ids, data, report_xml, context)
655
656     def create_single(self, cr, uid, ids, data, report_xml, context={}):
657         logo = None
658         context = context.copy()
659         pool = pooler.get_pool(cr.dbname)
660         want_header = self.header
661         title = report_xml.name
662         attach = report_xml.attachment
663         report_type = report_xml.report_type
664         want_header = report_xml.header
665
666         if report_type in ['sxw','odt']:
667             context['parents'] = sxw_parents
668             sxw_io = StringIO.StringIO(report_xml.report_sxw_content)
669             sxw_z = zipfile.ZipFile(sxw_io, mode='r')
670             rml = sxw_z.read('content.xml')
671             meta = sxw_z.read('meta.xml')
672             sxw_z.close()
673             rml_parser = self.parser(cr, uid, self.name2, context)
674             rml_parser.parents = sxw_parents
675             rml_parser.tag = sxw_tag
676             objs = self.getObjects(cr, uid, ids, context)
677             rml_parser.preprocess(objs, data, ids)
678             rml_dom = xml.dom.minidom.parseString(rml)
679
680             node = rml_dom.documentElement
681
682             elements = node.getElementsByTagName("text:p")
683
684             for pe in elements:
685                 e = pe.getElementsByTagName("text:drop-down")
686                 for de in e:
687                     pp=de.parentNode
688                     for cnd in de.childNodes:
689                         if cnd.nodeType in (cnd.CDATA_SECTION_NODE, cnd.TEXT_NODE):
690                             pe.appendChild(cnd)
691                             pp.removeChild(de)
692
693             # Add Information : Resource ID and Model
694             rml_dom_meta = xml.dom.minidom.parseString(meta)
695             node = rml_dom_meta.documentElement
696             elements = node.getElementsByTagName("meta:user-defined")
697             for pe in elements:
698                 if pe.hasAttribute("meta:name"):
699                     if pe.getAttribute("meta:name") == "Info 3":
700                         pe.childNodes[0].data=data['id']
701                     if pe.getAttribute("meta:name") == "Info 4":
702                         pe.childNodes[0].data=data['model']
703             meta = rml_dom_meta.documentElement.toxml('utf-8')
704
705             rml2 = rml_parser._parse(rml_dom, objs, data, header=want_header)
706             sxw_z = zipfile.ZipFile(sxw_io, mode='a')
707             sxw_z.writestr('content.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \
708                     rml2)
709             sxw_z.writestr('meta.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \
710                     meta)
711
712             if want_header:
713                 #Add corporate header/footer
714                 if report_type=='odt':
715                     rml = tools.file_open('custom/corporate_odt_header.xml').read()
716                 if report_type=='sxw':
717                     rml = tools.file_open('custom/corporate_sxw_header.xml').read()
718                 rml_parser = self.parser(cr, uid, self.name2, context)
719                 rml_parser.parents = sxw_parents
720                 rml_parser.tag = sxw_tag
721                 objs = self.getObjects(cr, uid, ids, context)
722                 rml_parser.preprocess(objs, data, ids)
723                 rml_dom = xml.dom.minidom.parseString(rml)
724                 rml2 = rml_parser._parse(rml_dom, objs, data, header=want_header)
725                 sxw_z.writestr('styles.xml',"<?xml version='1.0' encoding='UTF-8'?>" + \
726                         rml2)
727             sxw_z.close()
728             rml2 = sxw_io.getvalue()
729             sxw_io.close()
730         else:
731             rml = report_xml.report_rml_content
732             context['parents'] = rml_parents
733             rml_parser = self.parser(cr, uid, self.name2, context)
734             rml_parser.parents = rml_parents
735             rml_parser.tag = rml_tag
736             objs = self.getObjects(cr, uid, ids, context)
737             rml_parser.preprocess(objs, data, ids)
738             rml_dom = xml.dom.minidom.parseString(rml)
739             rml2 = rml_parser._parse(rml_dom, objs, data, header=want_header)
740             if rml_parser.logo:
741                 logo = base64.decodestring(rml_parser.logo)
742
743         create_doc = self.generators[report_type]
744         pdf = create_doc(rml2, logo, title.encode('utf8'))
745
746         return (pdf, report_type)
747