diff --git a/report_csv/__manifest__.py b/report_csv/__manifest__.py index 6f75d9cf4..0ee668415 100644 --- a/report_csv/__manifest__.py +++ b/report_csv/__manifest__.py @@ -9,7 +9,11 @@ "version": "15.0.1.0.0", "license": "AGPL-3", "depends": ["base", "web"], - "data": ["views/webclient_templates.xml"], "demo": ["demo/report.xml"], + "assets": { + "web.assets_backend": [ + "report_csv/static/src/js/report/qwebactionmanager.esm.js" + ] + }, "installable": True, } diff --git a/report_csv/controllers/main.py b/report_csv/controllers/main.py index 5a3c78c56..2aa5ad581 100644 --- a/report_csv/controllers/main.py +++ b/report_csv/controllers/main.py @@ -2,12 +2,23 @@ # License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html). import json +import logging -from odoo.http import content_disposition, request, route -from odoo.tools.safe_eval import safe_eval +from werkzeug.urls import url_decode + +from odoo.http import ( + content_disposition, + request, + route, + serialize_exception as _serialize_exception, +) +from odoo.tools import html_escape +from odoo.tools.safe_eval import safe_eval, time from odoo.addons.web.controllers import main as report +_logger = logging.getLogger(__name__) + class ReportController(report.ReportController): @route() @@ -28,29 +39,65 @@ class ReportController(report.ReportController): del data["context"]["lang"] context.update(data["context"]) csv = report.with_context(**context)._render_csv(docids, data=data)[0] - filename = "{}.{}".format(report.name, "csv") - if docids: - obj = request.env[report.model].browse(docids) - if report.print_report_name and not len(obj) > 1: - report_name = safe_eval( - report.print_report_name, - {"object": obj}, - ) - filename = "{}.{}".format(report_name, "csv") - # When we print multiple records we still allow a custom - # filename. - elif report.print_report_name and len(obj) > 1: - report_name = safe_eval( - report.print_report_name, - {"object": obj}, - ) - filename = "{}.{}".format(report_name, "csv") csvhttpheaders = [ ("Content-Type", "text/csv"), ("Content-Length", len(csv)), - ("Content-Disposition", content_disposition(filename)), ] return request.make_response(csv, headers=csvhttpheaders) return super(ReportController, self).report_routes( reportname, docids, converter, **data ) + + @route() + def report_download(self, data, context=None): + requestcontent = json.loads(data) + url, report_type = requestcontent[0], requestcontent[1] + try: + if report_type == "csv": + reportname = url.split("/report/csv/")[1].split("?")[0] + docids = None + if "/" in reportname: + reportname, docids = reportname.split("/") + if docids: + # Generic report: + response = self.report_routes( + reportname, docids=docids, converter="csv", context=context + ) + else: + # Particular report: + data = dict( + url_decode(url.split("?")[1]).items() + ) # decoding the args represented in JSON + if "context" in data: + context, data_context = json.loads(context or "{}"), json.loads( + data.pop("context") + ) + context = json.dumps({**context, **data_context}) + response = self.report_routes( + reportname, converter="csv", context=context, **data + ) + + report = request.env["ir.actions.report"]._get_report_from_name( + reportname + ) + filename = "%s.%s" % (report.name, "csv") + + if docids: + ids = [int(x) for x in docids.split(",")] + obj = request.env[report.model].browse(ids) + if report.print_report_name and not len(obj) > 1: + report_name = safe_eval( + report.print_report_name, {"object": obj, "time": time} + ) + filename = "%s.%s" % (report_name, "csv") + response.headers.add( + "Content-Disposition", content_disposition(filename) + ) + return response + else: + return super(ReportController, self).report_download(data, context) + except Exception as e: + _logger.exception("Error while generating report %s", reportname) + 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_csv/static/src/js/report/qwebactionmanager.esm.js b/report_csv/static/src/js/report/qwebactionmanager.esm.js new file mode 100644 index 000000000..797cdec27 --- /dev/null +++ b/report_csv/static/src/js/report/qwebactionmanager.esm.js @@ -0,0 +1,54 @@ +/** @odoo-module **/ + +import {download} from "@web/core/network/download"; +import {registry} from "@web/core/registry"; + +registry + .category("ir.actions.report handlers") + .add("csv_handler", async function (action, options, env) { + if (action.report_type === "csv") { + const type = action.report_type; + let url = `/report/${type}/${action.report_name}`; + const actionContext = action.context || {}; + if (action.data && JSON.stringify(action.data) !== "{}") { + // Build a query string with `action.data` (it's the place where reports + // using a wizard to customize the output traditionally put their options) + const action_options = encodeURIComponent(JSON.stringify(action.data)); + const context = encodeURIComponent(JSON.stringify(actionContext)); + url += `?options=${action_options}&context=${context}`; + } else { + if (actionContext.active_ids) { + url += `/${actionContext.active_ids.join(",")}`; + } + if (type === "csv") { + const context = encodeURIComponent( + JSON.stringify(env.services.user.context) + ); + url += `?context=${context}`; + } + } + env.services.ui.block(); + try { + await download({ + url: "/report/download", + data: { + data: JSON.stringify([url, action.report_type]), + context: JSON.stringify(env.services.user.context), + }, + }); + } finally { + env.services.ui.unblock(); + } + const onClose = options.onClose; + if (action.close_on_report_download) { + return env.services.action.doAction( + {type: "ir.actions.act_window_close"}, + {onClose} + ); + } else if (onClose) { + onClose(); + } + return Promise.resolve(true); + } + return Promise.resolve(false); + }); diff --git a/report_csv/static/src/js/report/qwebactionmanager.js b/report_csv/static/src/js/report/qwebactionmanager.js deleted file mode 100644 index 26f787d34..000000000 --- a/report_csv/static/src/js/report/qwebactionmanager.js +++ /dev/null @@ -1,98 +0,0 @@ -// © 2019 Creu Blanca -// License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html). -odoo.define("report_csv.report", function (require) { - "use strict"; - - var core = require("web.core"); - var ActionManager = require("web.ActionManager"); - var framework = require("web.framework"); - var session = require("web.session"); - var _t = core._t; - - ActionManager.include({ - _downloadReportCSV: function (url, actions) { - var self = this; - framework.blockUI(); - var type = "csv"; - var cloned_action = _.clone(actions); - var report_url = url; - - if ( - _.isUndefined(cloned_action.data) || - _.isNull(cloned_action.data) || - (_.isObject(cloned_action.data) && _.isEmpty(cloned_action.data)) - ) { - if (cloned_action.context.active_ids) { - report_url += "/" + cloned_action.context.active_ids.join(","); - } - } else { - report_url += - "?options=" + - encodeURIComponent(JSON.stringify(cloned_action.data)); - report_url += - "&context=" + - encodeURIComponent(JSON.stringify(cloned_action.context)); - } - - return new Promise(function (resolve, reject) { - var blocked = !session.get_file({ - url: report_url, - data: { - data: JSON.stringify([report_url, type]), - }, - success: resolve, - error: (error) => { - self.call("crash_manager", "rpc_error", error); - reject(); - }, - complete: framework.unblockUI, - }); - if (blocked) { - // AAB: this check should be done in get_file service directly, - // should not be the concern of the caller (and that way, get_file - // could return a deferred) - var message = _t( - "A popup window with your report was blocked. You " + - "may need to change your browser settings to allow " + - "popup windows for this page." - ); - this.do_warn(_t("Warning"), message, true); - } - }); - }, - - _triggerDownload: function (action, options, type) { - var self = this; - var reportUrls = this._makeReportUrls(action); - if (type === "csv") { - return this._downloadReportCSV(reportUrls[type], action).then( - function () { - if (action.close_on_report_download) { - var closeAction = {type: "ir.actions.act_window_close"}; - return self.doAction( - closeAction, - _.pick(options, "on_close") - ); - } - return options.on_close(); - } - ); - } - return this._super.apply(this, arguments); - }, - - _makeReportUrls: function (action) { - var reportUrls = this._super.apply(this, arguments); - reportUrls.csv = "/report/csv/" + action.report_name; - return reportUrls; - }, - - _executeReportAction: function (action, options) { - var self = this; - if (action.report_type === "csv") { - return self._triggerDownload(action, options, "csv"); - } - return this._super.apply(this, arguments); - }, - }); -}); diff --git a/report_csv/views/webclient_templates.xml b/report_csv/views/webclient_templates.xml deleted file mode 100644 index 62b6f161d..000000000 --- a/report_csv/views/webclient_templates.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - -