[MIG] report_csv: Migration to 15.0
parent
9137cf38cf
commit
bf22bf79e8
|
@ -9,7 +9,11 @@
|
||||||
"version": "15.0.1.0.0",
|
"version": "15.0.1.0.0",
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"depends": ["base", "web"],
|
"depends": ["base", "web"],
|
||||||
"data": ["views/webclient_templates.xml"],
|
|
||||||
"demo": ["demo/report.xml"],
|
"demo": ["demo/report.xml"],
|
||||||
|
"assets": {
|
||||||
|
"web.assets_backend": [
|
||||||
|
"report_csv/static/src/js/report/qwebactionmanager.esm.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
"installable": True,
|
"installable": True,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,23 @@
|
||||||
# License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
|
# License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from odoo.http import content_disposition, request, route
|
from werkzeug.urls import url_decode
|
||||||
from odoo.tools.safe_eval import safe_eval
|
|
||||||
|
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
|
from odoo.addons.web.controllers import main as report
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ReportController(report.ReportController):
|
class ReportController(report.ReportController):
|
||||||
@route()
|
@route()
|
||||||
|
@ -28,29 +39,65 @@ class ReportController(report.ReportController):
|
||||||
del data["context"]["lang"]
|
del data["context"]["lang"]
|
||||||
context.update(data["context"])
|
context.update(data["context"])
|
||||||
csv = report.with_context(**context)._render_csv(docids, data=data)[0]
|
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 = [
|
csvhttpheaders = [
|
||||||
("Content-Type", "text/csv"),
|
("Content-Type", "text/csv"),
|
||||||
("Content-Length", len(csv)),
|
("Content-Length", len(csv)),
|
||||||
("Content-Disposition", content_disposition(filename)),
|
|
||||||
]
|
]
|
||||||
return request.make_response(csv, headers=csvhttpheaders)
|
return request.make_response(csv, headers=csvhttpheaders)
|
||||||
return super(ReportController, self).report_routes(
|
return super(ReportController, self).report_routes(
|
||||||
reportname, docids, converter, **data
|
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)))
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
|
@ -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);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<odoo>
|
|
||||||
<!--
|
|
||||||
© 2019 Creu Blanca
|
|
||||||
License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
|
|
||||||
-->
|
|
||||||
<template id="assets_backend" inherit_id="web.assets_backend">
|
|
||||||
<xpath expr="." position="inside">
|
|
||||||
<script
|
|
||||||
type="text/javascript"
|
|
||||||
src="/report_csv/static/src/js/report/qwebactionmanager.js"
|
|
||||||
/>
|
|
||||||
</xpath>
|
|
||||||
</template>
|
|
||||||
</odoo>
|
|
|
@ -0,0 +1 @@
|
||||||
|
../../../../report_csv
|
|
@ -0,0 +1,6 @@
|
||||||
|
import setuptools
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
setup_requires=['setuptools-odoo'],
|
||||||
|
odoo_addon=True,
|
||||||
|
)
|
Loading…
Reference in New Issue