From 0fbd3538f9d55397db8fa4dc914316b4a097c8c7 Mon Sep 17 00:00:00 2001 From: Laurent Mignon Date: Fri, 17 Feb 2017 14:31:57 +0100 Subject: [PATCH] [IMP] report_py3o: Take into account print_report_name If a Printed Report Name is set on the action report, use it as downloaded filename refs #133 --- report_py3o/__init__.py | 1 + report_py3o/__manifest__.py | 1 + report_py3o/controllers/__init__.py | 1 + report_py3o/controllers/main.py | 100 ++++++++++++++++++ report_py3o/models/ir_actions_report_xml.py | 24 ++++- .../static/src/js/py3oactionmanager.js | 59 +++++++++++ report_py3o/views/report_py3o.xml | 10 ++ 7 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 report_py3o/controllers/__init__.py create mode 100644 report_py3o/controllers/main.py create mode 100644 report_py3o/static/src/js/py3oactionmanager.js create mode 100644 report_py3o/views/report_py3o.xml diff --git a/report_py3o/__init__.py b/report_py3o/__init__.py index 0650744f..f7209b17 100644 --- a/report_py3o/__init__.py +++ b/report_py3o/__init__.py @@ -1 +1,2 @@ from . import models +from . import controllers diff --git a/report_py3o/__manifest__.py b/report_py3o/__manifest__.py index 2f9aaaba..b42c1287 100644 --- a/report_py3o/__manifest__.py +++ b/report_py3o/__manifest__.py @@ -23,6 +23,7 @@ 'views/py3o_template.xml', 'views/py3o_server.xml', 'views/ir_report.xml', + 'views/report_py3o.xml', 'demo/report_py3o.xml', ], 'installable': True, diff --git a/report_py3o/controllers/__init__.py b/report_py3o/controllers/__init__.py new file mode 100644 index 00000000..12a7e529 --- /dev/null +++ b/report_py3o/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/report_py3o/controllers/main.py b/report_py3o/controllers/main.py new file mode 100644 index 00000000..71d1ca08 --- /dev/null +++ b/report_py3o/controllers/main.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import json +import mimetypes +from werkzeug import exceptions, url_decode + +from odoo.http import route, request + +from odoo.addons.report.controllers import main +from odoo.addons.web.controllers.main import ( + _serialize_exception, + content_disposition +) +from odoo.tools import html_escape + + +class ReportController(main.ReportController): + + @route() + def report_routes(self, reportname, docids=None, converter=None, **data): + if converter != 'py3o': + return super(ReportController, self).report_routes( + reportname=reportname, docids=docids, converter=converter, + **data) + context = dict(request.env.context) + + if docids: + docids = [int(i) for i in docids.split(',')] + if data.get('options'): + data.update(json.loads(data.pop('options'))) + if data.get('context'): + # Ignore 'lang' here, because the context in data is the + # one from the webclient *but* if the user explicitely wants to + # change the lang, this mechanism overwrites it. + data['context'] = json.loads(data['context']) + if data['context'].get('lang'): + del data['context']['lang'] + context.update(data['context']) + + ir_action = request.env['ir.actions.report.xml'] + action_py3o_report = ir_action.get_from_report_name( + reportname, "py3o").with_context(context) + if not action_py3o_report: + raise exceptions.HTTPException( + description='Py3o action report not found for report_name ' + '%s' % reportname) + context['report_name'] = reportname + py3o_report = request.env['py3o.report'].create({ + 'ir_actions_report_xml_id': action_py3o_report.id + }).with_context(context) + res, filetype = py3o_report.create_report(docids, data) + filename = action_py3o_report.gen_report_download_filename( + docids, data) + content_type = mimetypes.guess_type("x." + filetype)[0] + http_headers = [('Content-Type', content_type), + ('Content-Length', len(res)), + ('Content-Disposition', content_disposition(filename)) + ] + return request.make_response(res, headers=http_headers) + + @route() + def report_download(self, data, token): + """This function is used by 'qwebactionmanager.js' in order to trigger + the download of a py3o/controller report. + + :param data: a javascript array JSON.stringified containg report + internal url ([0]) and type [1] + :returns: Response with a filetoken cookie and an attachment header + """ + requestcontent = json.loads(data) + url, type = requestcontent[0], requestcontent[1] + if type != 'py3o': + return super(ReportController, self).report_download(data, token) + try: + reportname = url.split('/report/py3o/')[1].split('?')[0] + docids = None + if '/' in reportname: + reportname, docids = reportname.split('/') + + if docids: + # Generic report: + response = self.report_routes( + reportname, docids=docids, converter='py3o') + else: + # Particular report: + # decoding the args represented in JSON + data = url_decode(url.split('?')[1]).items() + response = self.report_routes( + reportname, converter='py3o', **dict(data)) + response.set_cookie('fileToken', token) + return response + except Exception, e: + se = _serialize_exception(e) + error = { + 'code': 200, + 'message': "Odoo Server Error", + 'data': se + } + return request.make_response(html_escape(json.dumps(error))) diff --git a/report_py3o/models/ir_actions_report_xml.py b/report_py3o/models/ir_actions_report_xml.py index 5539155c..66d9a5fc 100644 --- a/report_py3o/models/ir_actions_report_xml.py +++ b/report_py3o/models/ir_actions_report_xml.py @@ -2,8 +2,10 @@ # Copyright 2013 XCG Consulting (http://odoo.consulting) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging +import time from odoo import api, fields, models, _ from odoo.exceptions import ValidationError +from odoo.tools.safe_eval import safe_eval logger = logging.getLogger(__name__) @@ -87,14 +89,30 @@ class IrActionsReportXml(models.Model): "files as selected records. If you enable this option, Odoo will " "generate instead a single report for the selected records.") + @api.model + def get_from_report_name(self, report_name, report_type): + return self.search( + [("report_name", "=", report_name), + ("report_type", "=", report_type)]) + @api.model def render_report(self, res_ids, name, data): - action_py3o_report = self.search( - [("report_name", "=", name), - ("report_type", "=", "py3o")]) + action_py3o_report = self.get_from_report_name(name, "py3o") if action_py3o_report: return self.env['py3o.report'].create({ 'ir_actions_report_xml_id': action_py3o_report.id }).create_report(res_ids, data) return super(IrActionsReportXml, self).render_report( res_ids, name, data) + + @api.multi + def gen_report_download_filename(self, res_ids, data): + """Override this function to change the name of the downloaded report + """ + self.ensure_one() + report = self.get_from_report_name(self.report_name, self.report_type) + if report.print_report_name and not len(res_ids) > 1: + obj = self.env[self.model].browse(res_ids) + return safe_eval(report.print_report_name, + {'object': obj, 'time': time}) + return "%s.%s" % (self.name, self.py3o_filetype) diff --git a/report_py3o/static/src/js/py3oactionmanager.js b/report_py3o/static/src/js/py3oactionmanager.js new file mode 100644 index 00000000..8e9aa2eb --- /dev/null +++ b/report_py3o/static/src/js/py3oactionmanager.js @@ -0,0 +1,59 @@ +/* Copyright 2017 ACSONE SA/NV + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ +odoo.define('report_py3o.report', function (require) { + +var ActionManager = require('web.ActionManager'); +var core = require('web.core'); +var crash_manager = require('web.crash_manager'); +var framework = require('web.framework'); +var session = require('web.session'); + +var _t = core._t; + +var trigger_download = function(session, response, c, action, options) { + session.get_file({ + url: '/report/download', + data: {data: JSON.stringify(response)}, + complete: framework.unblockUI, + error: c.rpc_error.bind(c), + success: function(){ + if (action && options && !action.dialog) { + options.on_close(); + } + }, + }); +}; + +ActionManager.include({ + ir_actions_report_xml: function(action, options) { + var self = this; + framework.blockUI(); + action = _.clone(action); + _t = core._t; + + // Py3o reports + if ('report_type' in action && action.report_type == 'py3o' ) { + var report_url = '/report/py3o/' + action.report_name;; + // 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 { + report_url += "&options=" + encodeURIComponent(JSON.stringify(action.data)); + report_url += "&context=" + encodeURIComponent(JSON.stringify(action.context)); + } + + var response = new Array(); + response[0] = report_url; + response[1] = action.report_type; + var c = crash_manager; + return trigger_download(self.session, response, c, action, options); + } else { + return self._super(action, options); + } + } +}); + +}); diff --git a/report_py3o/views/report_py3o.xml b/report_py3o/views/report_py3o.xml new file mode 100644 index 00000000..d99fc0d8 --- /dev/null +++ b/report_py3o/views/report_py3o.xml @@ -0,0 +1,10 @@ + + + + + +