diff --git a/addons/report/controllers/main.py b/addons/report/controllers/main.py index f8435ba8245..d3a683cccdc 100644 --- a/addons/report/controllers/main.py +++ b/addons/report/controllers/main.py @@ -22,48 +22,43 @@ from openerp.addons.web.http import Controller, route, request import simplejson -import urlparse -from werkzeug import exceptions +from werkzeug import exceptions, url_decode +from werkzeug.test import Client +from werkzeug.wrappers import BaseResponse +from werkzeug.datastructures import Headers from reportlab.graphics.barcode import createBarcodeDrawing class ReportController(Controller): #------------------------------------------------------ - # Generic reports controller + # Report controllers #------------------------------------------------------ - @route('/report//', type='http', auth='user', website=True, multilang=True) - def report_html(self, reportname, docids): - cr, uid, context = request.cr, request.uid, request.context - docids = self._eval_params(docids) - return request.registry['report'].get_html(cr, uid, docids, reportname, context=context) - - @route('/report/pdf/report//', type='http', auth="user", website=True) - def report_pdf(self, reportname, docids): - cr, uid, context = request.cr, request.uid, request.context - docids = self._eval_params(docids) - pdf = request.registry['report'].get_pdf(cr, uid, docids, reportname, context=context) - pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))] - return request.make_response(pdf, headers=pdfhttpheaders) - - #------------------------------------------------------ - # Particular reports controller - #------------------------------------------------------ - @route('/report/', type='http', auth='user', website=True, multilang=True) - def report_html_particular(self, reportname, **data): - cr, uid, context = request.cr, request.uid, request.context + @route([ + '/report//', + '/report///', + ], type='http', auth='user', website=True, multilang=True) + def report_routes(self, reportname, docids=None, converter=None, **data): report_obj = request.registry['report'] - data = self._eval_params(data) # Sanitizing - return report_obj.get_html(cr, uid, [], reportname, data=data, context=context) - - @route('/report/pdf/report/', type='http', auth='user', website=True, multilang=True) - def report_pdf_particular(self, reportname, **data): cr, uid, context = request.cr, request.uid, request.context - report_obj = request.registry['report'] - data = self._eval_params(data) # Sanitizing - pdf = report_obj.get_pdf(cr, uid, [], reportname, data=data, context=context) - pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))] - return request.make_response(pdf, headers=pdfhttpheaders) + + if docids: + docids = [int(i) for i in docids.split(',')] + options_data = None + if data.get('options'): + options_data = simplejson.loads(data['options']) + if data.get('context'): + context.update(simplejson.loads(data['context'])) + + if converter == 'html': + html = report_obj.get_html(cr, uid, docids, reportname, data=options_data, context=context) + return request.make_response(html) + elif converter == 'pdf': + pdf = report_obj.get_pdf(cr, uid, docids, reportname, data=options_data, context=context) + pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))] + return request.make_response(pdf, headers=pdfhttpheaders) + else: + raise exceptions.HTTPException(description='Converter %s not implemented.' % converter) #------------------------------------------------------ # Misc. route utils @@ -94,7 +89,7 @@ class ReportController(Controller): @route(['/report/download'], type='http', auth="user", website=True) def report_download(self, data, token): """This function is used by 'qwebactionmanager.js' in order to trigger the download of - a pdf report. + a pdf/controller report. :param data: a javascript array JSON.stringified containg report internal url ([0]) and type [1] @@ -102,26 +97,26 @@ class ReportController(Controller): """ requestcontent = simplejson.loads(data) url, type = requestcontent[0], requestcontent[1] - if type == 'qweb-pdf': - reportname = url.split('/report/pdf/report/')[1].split('?')[0].split('/')[0] - if '?' not in url: + if type == 'qweb-pdf': + reportname = url.split('/report/pdf/')[1].split('?')[0] + + docids = None + if '/' in reportname: + reportname, docids = reportname.split('/') + + if docids: # Generic report: - docids = url.split('/')[-1] - response = self.report_pdf(reportname, docids) + response = self.report_routes(reportname, docids=docids, converter='pdf') else: # Particular report: - querystring = url.split('?')[1] - querystring = dict(urlparse.parse_qsl(querystring)) - response = self.report_pdf_particular(reportname, **querystring) + data = url_decode(url.split('?')[1]).items() # decoding the args represented in JSON + response = self.report_routes(reportname, converter='pdf', **dict(data)) response.headers.add('Content-Disposition', 'attachment; filename=%s.pdf;' % reportname) response.set_cookie('fileToken', token) return response elif type =='controller': - from werkzeug.test import Client - from werkzeug.wrappers import BaseResponse - from werkzeug.datastructures import Headers reqheaders = Headers(request.httprequest.headers) response = Client(request.httprequest.app, BaseResponse).get(url, headers=reqheaders, follow_redirects=True) response.set_cookie('fileToken', token) @@ -132,33 +127,3 @@ class ReportController(Controller): @route(['/report/check_wkhtmltopdf'], type='json', auth="user") def check_wkhtmltopdf(self): return request.registry['report']._check_wkhtmltopdf() - - def _eval_params(self, param): - """Parse a dict generated by the webclient (javascript) into a python dict. - """ - if isinstance(param, dict): - for key, value in param.iteritems(): - if value.lower() == 'false': - param[key] = False - elif value.lower() == 'true': - param[key] = True - elif ',' in value: - try: - param[key] = [int(i) for i in value.split(',')] - except ValueError: - param[key] = value.split(',') - if len(param[key]) == 1: - param[key] = value - else: - try: - param[key] = int(value) - except (ValueError, TypeError): - continue - else: - if isinstance(param, (str, unicode)): - param = [int(i) for i in param.split(',')] - if isinstance(param, list): - param = list(set(param)) - if isinstance(param, int): - param = [param] - return param diff --git a/addons/report/models/__init__.py b/addons/report/models/__init__.py index e048e10eb91..d21f03e5bcb 100644 --- a/addons/report/models/__init__.py +++ b/addons/report/models/__init__.py @@ -1,2 +1,3 @@ import report import report_paperformat +import abstract_report diff --git a/addons/report/models/abstract_report.py b/addons/report/models/abstract_report.py new file mode 100644 index 00000000000..c84ce1cac50 --- /dev/null +++ b/addons/report/models/abstract_report.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import osv + + +class AbstractReport(osv.AbstractModel): + """Model used to embed old style reports""" + _name = 'report.abstract_report' + _template = None + _wrapped_report_class = None + + def render_html(self, cr, uid, ids, data=None, context=None): + if context is None: + context = {} + + # If the key 'landscape' is present in data['form'], passing it into the context + if data and data.get('form', {}).get('landscape'): + context['landscape'] = True + + if context and context.get('active_ids'): + # Browse the selected objects via their reference in context + model = context.get('active_model') or context.get('model') + objects_model = self.pool[model] + objects = objects_model.browse(cr, uid, context['active_ids'], context=context) + else: + # If no context is set (for instance, during test execution), build one + model = self.pool['report']._get_report_from_name(cr, uid, self._template).model + objects_model = self.pool[model] + objects = objects_model.browse(cr, uid, ids, context=context) + context['active_model'] = model + context['active_ids'] = ids + + # Generate the old style report + wrapped_report = self._wrapped_report_class(cr, uid, '', context=context) + wrapped_report.set_context(objects, data, ids) + + # Rendering self._template with the wrapped report instance localcontext as + # rendering environment + docargs = wrapped_report.localcontext + docargs['docs'] = docargs.get('objects') + return self.pool['report'].render(cr, uid, [], self._template, docargs, context=context) diff --git a/addons/report/models/report.py b/addons/report/models/report.py index b93bbe09f89..992842ffacd 100644 --- a/addons/report/models/report.py +++ b/addons/report/models/report.py @@ -20,9 +20,8 @@ ############################################################################## from openerp.osv import osv +from openerp.tools import config from openerp.tools.translate import _ -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, config -from openerp.osv.fields import float as float_field, function as function_field, datetime as datetime_field import os import time @@ -34,8 +33,6 @@ import tempfile import lxml.html import cStringIO import subprocess -from datetime import datetime -from functools import partial from distutils.version import LooseVersion try: from pyPdf import PdfFileWriter, PdfFileReader @@ -46,7 +43,7 @@ except ImportError: _logger = logging.getLogger(__name__) -"""Check the presence of wkhtmltopdf and return its version.""" +"""Check the presence of wkhtmltopdf and return its version at OpnerERP start-up.""" wkhtmltopdf_state = 'install' try: process = subprocess.Popen( @@ -70,106 +67,26 @@ class Report(osv.Model): public_user = None + MINIMAL_HTML_PAGE = """ + + + + + + + + + + {subst} + + + {body} + +""" + #-------------------------------------------------------------------------- # Extension of ir_ui_view.render with arguments frequently used in reports #-------------------------------------------------------------------------- - - def _get_digits(self, cr=None, uid=None, obj=None, f=None, dp=None): - d = DEFAULT_DIGITS = 2 - if dp: - decimal_precision_obj = self.pool['decimal.precision'] - ids = decimal_precision_obj.search(cr, uid, [('name', '=', dp)]) - if ids: - d = decimal_precision_obj.browse(cr, uid, ids)[0].digits - elif obj and f: - res_digits = getattr(obj._columns[f], 'digits', lambda x: ((16, DEFAULT_DIGITS))) - if isinstance(res_digits, tuple): - d = res_digits[1] - else: - d = res_digits(cr)[1] - elif (hasattr(obj, '_field') and - isinstance(obj._field, (float_field, function_field)) and - obj._field.digits): - d = obj._field.digits[1] or DEFAULT_DIGITS - return d - - def _get_lang_dict(self, cr, uid): - pool_lang = self.pool['res.lang'] - lang = self.localcontext.get('lang', 'en_US') or 'en_US' - lang_ids = pool_lang.search(cr, uid, [('code', '=', lang)])[0] - lang_obj = pool_lang.browse(cr, uid, lang_ids) - lang_dict = { - 'lang_obj': lang_obj, - 'date_format': lang_obj.date_format, - 'time_format': lang_obj.time_format - } - self.lang_dict.update(lang_dict) - self.default_lang[lang] = self.lang_dict.copy() - return True - - def formatLang(self, value, digits=None, date=False, date_time=False, grouping=True, monetary=False, dp=False, currency_obj=False, cr=None, uid=None): - """ - Assuming 'Account' decimal.precision=3: - formatLang(value) -> digits=2 (default) - formatLang(value, digits=4) -> digits=4 - formatLang(value, dp='Account') -> digits=3 - formatLang(value, digits=5, dp='Account') -> digits=5 - """ - def get_date_length(date_format=DEFAULT_SERVER_DATE_FORMAT): - return len((datetime.now()).strftime(date_format)) - - # In case we use formatLang on the model (and not in the rendering environment). - if not hasattr(self, 'land_ditct'): - self.localcontext = {} - self.lang_dict = {} - self.default_lang = {} - self._get_lang_dict(cr, uid) - self.lang_dict_called = True - - if digits is None: - if dp: - digits = self._get_digits(cr, uid, dp=dp) - else: - digits = self._get_digits(cr, uid, value) - - if isinstance(value, (str, unicode)) and not value: - return '' - - if not self.lang_dict_called: - self._get_lang_dict(cr, uid) - self.lang_dict_called = True - - if date or date_time: - if not str(value): - return '' - - date_format = self.lang_dict['date_format'] - parse_format = DEFAULT_SERVER_DATE_FORMAT - if date_time: - value = value.split('.')[0] - date_format = date_format + " " + self.lang_dict['time_format'] - parse_format = DEFAULT_SERVER_DATETIME_FORMAT - if isinstance(value, basestring): - # FIXME: the trimming is probably unreliable if format includes day/month names - # and those would need to be translated anyway. - date = datetime.strptime(value[:get_date_length(parse_format)], parse_format) - elif isinstance(value, time.struct_time): - date = datetime(*value[:6]) - else: - date = datetime(*value.timetuple()[:6]) - if date_time: - # Convert datetime values to the expected client/context timezone - date = datetime_field.context_timestamp(cr, uid, timestamp=date, context=self.localcontext) - return date.strftime(date_format.encode('utf-8')) - - res = self.lang_dict['lang_obj'].format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary) - if currency_obj: - if currency_obj.position == 'after': - res = '%s %s' % (res, currency_obj.symbol) - elif currency_obj and currency_obj.position == 'before': - res = '%s %s' % (currency_obj.symbol, res) - return res - def render(self, cr, uid, ids, template, values=None, context=None): """Allow to render a QWeb template python-side. This function returns the 'ir.ui.view' render but embellish it with some variables/methods used in reports. @@ -183,15 +100,6 @@ class Report(osv.Model): if context is None: context = {} - self.lang_dict = self.default_lang = {} - self.lang_dict_called = False - self.localcontext = { - 'lang': context.get('lang'), - 'tz': context.get('tz'), - 'uid': context.get('uid'), - } - self._get_lang_dict(cr, uid) - view_obj = self.pool['ir.ui.view'] def render_doc(doc_id, model, template): @@ -217,22 +125,20 @@ class Report(osv.Model): qcontext['o'] = self.pool[model].browse(cr, uid, doc_id, context=ctx) return view_obj.render(cr, uid, template, qcontext, context=ctx) + user = self.pool['res.users'].browse(cr, uid, uid) values.update({ 'time': time, - 'formatLang': partial(self.formatLang, cr=cr, uid=uid), - 'get_digits': partial(self._get_digits, cr=cr, uid=uid), 'render_doc': render_doc, 'editable': True, # Will active inherit_branding - 'res_company': self.pool['res.users'].browse(cr, uid, uid).company_id, + 'user': user, + 'res_company': user.company_id, 'website': False, # Will be overidden by ir.ui.view if the request has website enabled }) - return view_obj.render(cr, uid, template, values, context=context) #-------------------------------------------------------------------------- - # Main reports methods + # Main report methods #-------------------------------------------------------------------------- - def get_html(self, cr, uid, ids, report_name, data=None, context=None): """This method generates and returns html version of a report. """ @@ -241,7 +147,7 @@ class Report(osv.Model): try: report_model_name = 'report.%s' % report_name particularreport_obj = self.pool[report_model_name] - return particularreport_obj.render_html(cr, uid, ids, data={'form': data}, context=context) + return particularreport_obj.render_html(cr, uid, ids, data=data, context=context) except KeyError: report = self._get_report_from_name(cr, uid, report_name) report_obj = self.pool[report.model] @@ -262,41 +168,12 @@ class Report(osv.Model): if html is None: html = self.get_html(cr, uid, ids, report_name, data=data, context=context) - html = html.decode('utf-8') + html = html.decode('utf-8') # Ensure the current document is utf-8 encoded. # Get the ir.actions.report.xml record we are working on. report = self._get_report_from_name(cr, uid, report_name) - - # Check attachment_use field. If set to true and an existing pdf is already saved, load - # this one now. Else, mark save it. - save_in_attachment = {} - - if report.attachment_use is True: - save_in_attachment['model'] = report.model - save_in_attachment['loaded_documents'] = {} - - for record_id in ids: - obj = self.pool[report.model].browse(cr, uid, record_id) - filename = eval(report.attachment, {'object': obj, 'time': time}) - - if filename is False: # May be false if, for instance, the record is in draft state - continue - else: - alreadyindb = [('datas_fname', '=', filename), - ('res_model', '=', report.model), - ('res_id', '=', record_id)] - - attach_ids = self.pool['ir.attachment'].search(cr, uid, alreadyindb) - if attach_ids: - # Add the loaded pdf in the loaded_documents list - pdf = self.pool['ir.attachment'].browse(cr, uid, attach_ids[0]).datas - pdf = base64.decodestring(pdf) - save_in_attachment['loaded_documents'][record_id] = pdf - _logger.info('The PDF document %s was loaded from the database' % filename) - else: - # Mark current document to be saved - save_in_attachment[id] = filename - + # Check if we have to save the report or if we have to get one from the db. + save_in_attachment = self._check_attachment_use(cr, uid, ids, report) # Get the paperformat associated to the report, otherwise fallback on the company one. if not report.paperformat_id: user = self.pool['res.users'].browse(cr, uid, uid) @@ -305,34 +182,15 @@ class Report(osv.Model): paperformat = report.paperformat_id # Preparing the minimal html pages - #subst = self._get_url_content('/report/static/src/js/subst.js')[0] # Used in age numbering subst = " " css = '' # Will contain local css - headerhtml = [] contenthtml = [] footerhtml = [] base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') - minimalhtml = """ - - - - - - - - - - {subst} - - - {body} - -""" - - # The retrieved html report must be simplified. We convert it into a xml tree - # via lxml in order to extract headers, footers and content. + # The received html report must be simplified. We convert it in a xml tree + # in order to extract headers, bodies and footers. try: root = lxml.html.fromstring(html) @@ -341,12 +199,12 @@ class Report(osv.Model): for node in root.xpath("//div[@class='header']"): body = lxml.html.tostring(node) - header = minimalhtml.format(css=css, subst=subst, body=body, base_url=base_url) + header = self.MINIMAL_HTML_PAGE.format(css=css, subst=subst, body=body, base_url=base_url) headerhtml.append(header) for node in root.xpath("//div[@class='footer']"): body = lxml.html.tostring(node) - footer = minimalhtml.format(css=css, subst=subst, body=body, base_url=base_url) + footer = self.MINIMAL_HTML_PAGE.format(css=css, subst=subst, body=body, base_url=base_url) footerhtml.append(footer) for node in root.xpath("//div[@class='page']"): @@ -363,7 +221,7 @@ class Report(osv.Model): reportid = False body = lxml.html.tostring(node) - reportcontent = minimalhtml.format(css=css, subst='', body=body, base_url=base_url) + reportcontent = self.MINIMAL_HTML_PAGE.format(css=css, subst='', body=body, base_url=base_url) contenthtml.append(tuple([reportid, reportcontent])) except lxml.etree.XMLSyntaxError: @@ -390,37 +248,60 @@ class Report(osv.Model): :param report_name: Name of the template to generate an action for """ - # TODO: return the action for the ids passed in args if context is None: context = {} - if data is None: - data = {} - - report_obj = self.pool.get('ir.actions.report.xml') + report_obj = self.pool['ir.actions.report.xml'] idreport = report_obj.search(cr, uid, [('report_name', '=', report_name)], context=context) - try: report = report_obj.browse(cr, uid, idreport[0], context=context) except IndexError: - raise osv.except_osv(_('Bad Report'), - _('This report is not loaded into the database.')) + raise osv.except_osv(_('Bad Report'), _('This report is not loaded into the database.')) action = { + 'context': context, + 'data': data, 'type': 'ir.actions.report.xml', 'report_name': report.report_name, 'report_type': report.report_type, 'report_file': report.report_file, } - - if data: - action['datas'] = data - return action #-------------------------------------------------------------------------- # Report generation helpers #-------------------------------------------------------------------------- + def _check_attachment_use(self, cr, uid, ids, report): + """ Check attachment_use field. If set to true and an existing pdf is already saved, load + this one now. Else, mark save it. + """ + save_in_attachment = {} + if report.attachment_use is True: + save_in_attachment['model'] = report.model + save_in_attachment['loaded_documents'] = {} + + for record_id in ids: + obj = self.pool[report.model].browse(cr, uid, record_id) + filename = eval(report.attachment, {'object': obj, 'time': time}) + + if filename is False: # May be false if, for instance, the record is in draft state + continue + else: + alreadyindb = [('datas_fname', '=', filename), + ('res_model', '=', report.model), + ('res_id', '=', record_id)] + + attach_ids = self.pool['ir.attachment'].search(cr, uid, alreadyindb) + if attach_ids: + # Add the loaded pdf in the loaded_documents list + pdf = self.pool['ir.attachment'].browse(cr, uid, attach_ids[0]).datas + pdf = base64.decodestring(pdf) + save_in_attachment['loaded_documents'][record_id] = pdf + _logger.info('The PDF document %s was loaded from the database' % filename) + else: + # Mark current document to be saved + save_in_attachment[record_id] = filename + return save_in_attachment def _check_wkhtmltopdf(self): return wkhtmltopdf_state @@ -445,7 +326,8 @@ class Report(osv.Model): # Passing the cookie to wkhtmltopdf in order to resolve URL. try: from openerp.addons.web.http import request - command_args.extend(['--cookie', 'session_id', request.session.sid]) + if request: + command_args.extend(['--cookie', 'session_id', request.session.sid]) except AttributeError: pass @@ -453,6 +335,8 @@ class Report(osv.Model): if paperformat: command_args.extend(self._build_wkhtmltopdf_args(paperformat, spec_paperformat_args)) + command_args.extend(['--load-error-handling', 'ignore']) + if landscape and '--orientation' in command_args: command_args_copy = list(command_args) for index, elem in enumerate(command_args_copy): @@ -472,7 +356,7 @@ class Report(osv.Model): # Directly load the document if we have it if save_in_attachment and save_in_attachment['loaded_documents'].get(reporthtml[0]): pdfreport.write(save_in_attachment['loaded_documents'].get(reporthtml[0])) - pdfreport.flush() + pdfreport.seek(0) pdfdocuments.append(pdfreport) continue @@ -481,7 +365,7 @@ class Report(osv.Model): head_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.header.tmp.', dir=tmp_dir, mode='w+') head_file.write(headers[index]) - head_file.flush() + head_file.seek(0) command_arg_local.extend(['--header-html', head_file.name]) # Footer stuff @@ -489,14 +373,14 @@ class Report(osv.Model): foot_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.footer.tmp.', dir=tmp_dir, mode='w+') foot_file.write(footers[index]) - foot_file.flush() + foot_file.seek(0) command_arg_local.extend(['--footer-html', foot_file.name]) # Body stuff content_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.body.tmp.', dir=tmp_dir, mode='w+') content_file.write(reporthtml[1]) - content_file.flush() + content_file.seek(0) try: # If the server is running with only one worker, ask to create a secund to be able @@ -533,7 +417,7 @@ class Report(osv.Model): _logger.info('The PDF document %s is now saved in the ' 'database' % attachment['name']) - pdfreport.flush() + pdfreport.seek(0) pdfdocuments.append(pdfreport) if headers: diff --git a/addons/report/static/src/js/qwebactionmanager.js b/addons/report/static/src/js/qwebactionmanager.js index b75003942d2..aa98b2c9c52 100644 --- a/addons/report/static/src/js/qwebactionmanager.js +++ b/addons/report/static/src/js/qwebactionmanager.js @@ -6,8 +6,6 @@ openerp.report = function(instance) { var self = this; instance.web.blockUI(); action = _.clone(action); - var eval_contexts = ([instance.session.user_context] || []).concat([action.context]); - action.context = instance.web.pyeval.eval('contexts',eval_contexts); _t = instance.web._t; // QWeb reports @@ -15,43 +13,33 @@ openerp.report = function(instance) { var report_url = ''; switch (action.report_type) { case 'qweb-html': - report_url = '/report/' + action.report_name; + report_url = '/report/html/' + action.report_name; break; case 'qweb-pdf': - report_url = '/report/pdf/report/' + action.report_name; + report_url = '/report/pdf/' + action.report_name; break; case 'controller': report_url = action.report_file; break; default: - report_url = '/report/' + action.report_name; + report_url = '/report/html/' + action.report_name; break; } - // single/multiple id(s): no query string - // wizard: query string of action.datas.form - if (!('datas' in action)) { + // generic report: no query string + // particular: query string of action.data.form and context + if (!('data' in action) || !(action.data)) { if ('active_ids' in action.context) { report_url += "/" + action.context.active_ids.join(','); } } else { - _.each(action.datas.form, function(value, key) { - // will be erased when all wizards are rewritten - if (key.substring(0, 12) === 'used_context') { - delete action.datas.form[key]; - } - - if ($.type(value) === 'array') { - action.datas.form[key] = value.join(','); - } - }); - report_url += "?" + $.param(action.datas.form); + report_url += "?options=" + encodeURI(JSON.stringify(action.data)); + report_url += "&context=" + encodeURI(JSON.stringify(action.context)); } + if (action.report_type == 'qweb-html') { - // Open the html report in a popup window.open(report_url, '_blank', 'height=900,width=1280'); instance.web.unblockUI(); - return; } else { // Trigger the download of the pdf/controller report var c = openerp.webclient.crashmanager; @@ -68,6 +56,7 @@ openerp.report = function(instance) { wkhtmltopdf.org'), true); window.open(report_url.substring(12), '_blank', 'height=768,width=1024'); instance.web.unblockUI(); + return; } else { if (presence == 'upgrade') { self.do_notify(_t('Report'), _t('You should upgrade your version of\ @@ -75,22 +64,15 @@ openerp.report = function(instance) { support for table-breaking between pages.

wkhtmltopdf.org'), true); } - self.session.get_file({ - url: '/report/download', - data: {data: JSON.stringify(response)}, - complete: openerp.web.unblockUI, - error: c.rpc_error.bind(c) - }); } }); - } else { - self.session.get_file({ - url: '/report/download', - data: {data: JSON.stringify(response)}, - complete: openerp.web.unblockUI, - error: c.rpc_error.bind(c) - }); - } + } + self.session.get_file({ + url: '/report/download', + data: {data: JSON.stringify(response)}, + complete: openerp.web.unblockUI, + error: c.rpc_error.bind(c) + }); } } else { return self._super(action, options); diff --git a/addons/report/tests/test_reports.py b/addons/report/tests/test_reports.py index dda14442709..b1853489573 100644 --- a/addons/report/tests/test_reports.py +++ b/addons/report/tests/test_reports.py @@ -1,5 +1,23 @@ # -*- coding: utf-8 -*- - +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2014-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## import logging import openerp @@ -11,26 +29,24 @@ _logger = logging.getLogger(__name__) @openerp.tests.common.post_install(True) class TestReports(openerp.tests.TransactionCase): def test_reports(self): - return # commented out until post_install tests are working - registry, cr, uid = self.registry, self.cr, self.uid r_model = registry('ir.actions.report.xml') domain = [('report_type', 'like', 'qweb')] for r in r_model.browse(cr, uid, r_model.search(cr, uid, domain)): report_model = 'report.%s' % r.report_name - particular_model = registry('ir.model').search(cr, uid, [('model', '=', report_model)]) - + try: + registry(report_model) + except KeyError: # Only test the generic reports here - if particular_model: + _logger.info("testing report %s", r.report_name) + report_model = registry(r.model) + report_model_ids = report_model.search(cr, uid, [], limit=10) + if not report_model_ids: + _logger.info("no record found skipping report %s", r.report_name) + if not r.multi: + report_model_ids = report_model_ids[:1] + + # Test report generation + registry('report').get_html(cr, uid, report_model_ids, r.report_name) + else: continue - - _logger.info("testing report %s", r.report_name) - report_model = registry(r.model) - report_model_ids = report_model.search(cr, uid, [], limit=10) - if not report_model_ids: - _logger.info("no record found skipping report %s", r.report_name) - if not r.multi: - report_model_ids = report_model_ids[:1] - - # Test report generation - registry('report').get_html(cr, uid, report_model_ids, r.report_name)