11# -*- coding: utf-8 -*-
22# Copyright 2013 XCG Consulting (http://odoo.consulting)
3+ # Copyright 2016 ACSONE SA/NV
34# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
5+ import base64
6+ from base64 import b64decode
47from cStringIO import StringIO
58import json
6- import pkg_resources
9+ import logging
710import os
8- import sys
9- from base64 import b64decode
11+ import pkg_resources
1012import requests
13+ import sys
1114from tempfile import NamedTemporaryFile
12- from openerp import _
13- from openerp import exceptions
14- from openerp .report .report_sxw import report_sxw
15- from openerp import registry
16- import logging
15+
16+ from openerp .exceptions import UserError
17+ from openerp .report .report_sxw import rml_parse
18+ from openerp import api , fields , models , _
1719
1820logger = logging .getLogger (__name__ )
1921
2022try :
2123 from py3o .template .helpers import Py3oConvertor
2224 from py3o .template import Template
25+ from py3o import formats
2326except ImportError :
2427 logger .debug ('Cannot import py3o.template' )
2528try :
@@ -64,42 +67,46 @@ def defautl_extend(report_xml, localcontext):
6467 localcontext ['b64decode' ] = b64decode
6568
6669
67- class Py3oParser (report_sxw ):
68- """Custom class that use Py3o to render libroffice reports.
69- Code partially taken from CampToCamp's webkit_report."""
70+ class Py3oReport (models .TransientModel ):
71+ _name = "py3o.report"
72+ _inherit = 'report'
73+ _description = "Report Py30"
7074
71- def get_template (self , report_obj ):
75+ ir_actions_report_xml_id = fields .Many2one (
76+ comodel_name = "ir.actions.report.xml" ,
77+ required = True
78+ )
79+
80+ @api .multi
81+ def get_template (self ):
7282 """private helper to fetch the template data either from the database
7383 or from the default template file provided by the implementer.
7484
7585 ATM this method takes a report definition recordset
7686 to try and fetch the report template from database. If not found it
7787 will fallback to the template file referenced in the report definition.
7888
79- @param report_obj: a recordset representing the report defintion
80- @type report_obj: openerp.model.recordset instance
81-
8289 @returns: string or buffer containing the template data
8390
8491 @raises: TemplateNotFound which is a subclass of
8592 openerp.exceptions.DeferredException
8693 """
87-
94+ self . ensure_one ()
8895 tmpl_data = None
89-
90- if report_obj .py3o_template_id and report_obj .py3o_template_id .id :
96+ report_xml = self . ir_actions_report_xml_id
97+ if report_xml .py3o_template_id and report_xml .py3o_template_id .id :
9198 # if a user gave a report template
9299 tmpl_data = b64decode (
93- report_obj .py3o_template_id .py3o_template_data
100+ report_xml .py3o_template_id .py3o_template_data
94101 )
95102
96- elif report_obj .py3o_template_fallback :
97- tmpl_name = report_obj .py3o_template_fallback
103+ elif report_xml .py3o_template_fallback :
104+ tmpl_name = report_xml .py3o_template_fallback
98105 flbk_filename = None
99- if report_obj .module :
106+ if report_xml .module :
100107 # if the default is defined
101108 flbk_filename = pkg_resources .resource_filename (
102- "openerp.addons.%s" % report_obj .module ,
109+ "openerp.addons.%s" % report_xml .module ,
103110 tmpl_name ,
104111 )
105112 elif os .path .isabs (tmpl_name ):
@@ -119,37 +126,55 @@ def get_template(self, report_obj):
119126
120127 return tmpl_data
121128
122- def _extend_parser_context (self , parser_instance , report_xml ):
129+ @api .multi
130+ def _extend_parser_context (self , context_instance , report_xml ):
123131 # add default extenders
124132 for fct in _extender_functions .get (None , []):
125- fct (report_xml , parser_instance .localcontext )
133+ fct (report_xml , context_instance .localcontext )
126134 # add extenders for registered on the template
127135 xml_id = report_xml .get_external_id ().get (report_xml .id )
128136 if xml_id in _extender_functions :
129137 for fct in _extender_functions [xml_id ]:
130- fct (report_xml , parser_instance .localcontext )
138+ fct (report_xml , context_instance .localcontext )
139+
140+ @api .multi
141+ def _get_parser_context (self , model_instance , data ):
142+ report_xml = self .ir_actions_report_xml_id
143+ context_instance = rml_parse (self .env .cr , self .env .uid ,
144+ report_xml .name ,
145+ context = self .env .context )
146+ context_instance .set_context (model_instance , data , model_instance .ids ,
147+ report_xml .report_type )
148+ self ._extend_parser_context (context_instance , report_xml )
149+ return context_instance .localcontext
150+
151+ @api .multi
152+ def _postprocess_report (self , res , model_instance , save_in_attachment ):
153+ res_id = model_instance .id
154+ if save_in_attachment .get (res_id ):
155+ attachment = {
156+ 'name' : save_in_attachment .get (res_id ),
157+ 'datas' : base64 .encodestring (res ),
158+ 'datas_fname' : save_in_attachment .get (res_id ),
159+ 'res_model' : save_in_attachment .get ('model' ),
160+ 'res_id' : res_id ,
161+ }
162+ self .env ['ir.attachment' ].create (attachment )
163+ return res , "." + self .ir_actions_report_xml_id .py3o_filetype
131164
132- def create_single_pdf (self , cr , uid , ids , data , report_xml , context = None ):
133- """ Overide this function to generate our py3o report
165+ @api .multi
166+ def _create_single_report (self , model_instance , data , save_in_attachment ):
167+ """ This function to generate our py3o report
134168 """
135- if report_xml .report_type != 'py3o' :
136- return super (Py3oParser , self ).create_single_pdf (
137- cr , uid , ids , data , report_xml , context = context
138- )
139-
140- parser_instance = self .parser (cr , uid , self .name2 , context = context )
141- parser_instance .set_context (
142- self .getObjects (cr , uid , ids , context ),
143- data , ids , report_xml .report_type
144- )
145- self ._extend_parser_context (parser_instance , report_xml )
169+ self .ensure_one ()
170+ report_xml = self .ir_actions_report_xml_id
146171
147- tmpl_data = self .get_template (report_xml )
172+ tmpl_data = self .get_template ()
148173
149174 in_stream = StringIO (tmpl_data )
150175 out_stream = StringIO ()
151176 template = Template (in_stream , out_stream , escape_false = True )
152- localcontext = parser_instance . localcontext
177+ localcontext = self . _get_parser_context ( model_instance , data )
153178 if report_xml .py3o_is_local_fusion :
154179 template .render (localcontext )
155180 in_stream = out_stream
@@ -181,7 +206,7 @@ def create_single_pdf(self, cr, uid, ids, data, report_xml, context=None):
181206 report_xml .py3o_server_id .url , data = fields , files = files )
182207 if r .status_code != 200 :
183208 # server says we have an issue... let's tell that to enduser
184- raise exceptions . Warning (
209+ raise UserError (
185210 _ ('Fusion server error %s' ) % r .text ,
186211 )
187212
@@ -197,30 +222,62 @@ def create_single_pdf(self, cr, uid, ids, data, report_xml, context=None):
197222 fd .seek (0 )
198223 # ... but odoo wants the whole data in memory anyways :)
199224 res = fd .read ()
200-
201- return res , "." + filetype
202-
203- def create (self , cr , uid , ids , data , context = None ):
225+ return self ._postprocess_report (
226+ res , model_instance , save_in_attachment )
227+
228+ @api .multi
229+ def _get_or_create_single_report (self , model_instance , data ,
230+ save_in_attachment ):
231+ self .ensure_one ()
232+ if save_in_attachment and save_in_attachment [
233+ 'loaded_documents' ].get (model_instance .id ):
234+ d = save_in_attachment [
235+ 'loaded_documents' ].get (model_instance .id )
236+ return d , self .ir_actions_report_xml_id .py3o_filetype
237+ return self ._create_single_report (
238+ model_instance , data , save_in_attachment )
239+
240+ @api .multi
241+ def create_report (self , res_ids , data ):
204242 """ Override this function to handle our py3o report
205243 """
206- pool = registry (cr .dbname )
207- ir_action_report_obj = pool ['ir.actions.report.xml' ]
208- report_xml_ids = ir_action_report_obj .search (
209- cr , uid , [('report_name' , '=' , self .name [7 :])], context = context
210- )
211- if not report_xml_ids :
212- return super (Py3oParser , self ).create (
213- cr , uid , ids , data , context = context
214- )
215-
216- report_xml = ir_action_report_obj .browse (
217- cr , uid , report_xml_ids [0 ], context = context
218- )
219-
220- result = self .create_source_pdf (
221- cr , uid , ids , data , report_xml , context
222- )
223-
224- if not result :
225- return False , False
226- return result
244+ if len (res_ids ) > 1 and self .ir_actions_report_xml_id .py3o_filetype \
245+ != formats .FORMAT_PDF :
246+ raise UserError (
247+ _ ('Generating multiple reports at once is only supported '
248+ 'with output format %s' ) % formats .FORMAT_PDF )
249+ model_instances = self .env [self .ir_actions_report_xml_id .model ].browse (
250+ res_ids )
251+ save_in_attachment = self ._check_attachment_use (
252+ model_instances , self .ir_actions_report_xml_id ) or {}
253+ if len (model_instances ) == 1 :
254+ return self ._get_or_create_single_report (
255+ model_instances [0 ], data , save_in_attachment )
256+ results = []
257+ for model_instance in model_instances :
258+ results .add (self ._get_or_create_single_report (
259+ model_instance , data , save_in_attachment ))
260+ if results :
261+ from pyPdf import PdfFileWriter , PdfFileReader
262+ output = PdfFileWriter ()
263+ for r in results :
264+ reader = PdfFileReader (StringIO (r [0 ]))
265+ for page in range (reader .getNumPages ()):
266+ output .addPage (reader .getPage (page ))
267+ s = StringIO ()
268+ output .write (s )
269+ return s .getvalue (), results [0 ][1 ]
270+ return False , False
271+
272+ # the 2 overrides below are required since in the base class the v8 method
273+ # calls the v7 one with the context as parameter but the v7 one has not
274+ # the context declared in the these paramaters
275+ @api .v7
276+ def _check_attachment_use (self , cr , uid , ids , report ):
277+ return super (Py3oReport , self )._check_attachment_use (
278+ cr , uid , ids , report )
279+
280+ @api .v8
281+ def _check_attachment_use (self , records , report ):
282+ return Py3oReport ._check_attachment_use (
283+ self ._model , self ._cr , self ._uid , records .ids , report )
0 commit comments