From 651813a6475c7b6b675b8b008884e7975dc11442 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Mon, 12 Dec 2022 12:15:16 +0100 Subject: [PATCH] [MIG] report_py3o to v16 --- report_py3o/__manifest__.py | 5 ++-- report_py3o/controllers/main.py | 17 ++++++------- report_py3o/demo/report_py3o.xml | 3 +++ report_py3o/models/ir_actions_report.py | 11 ++++---- report_py3o/models/py3o_report.py | 31 ++++++++++++++++++++--- report_py3o/tests/test_report_py3o.py | 24 +++++++++--------- report_py3o/views/menu.xml | 4 --- report_py3o/views/py3o_template.xml | 7 ++--- requirements.txt | 3 +++ setup/report_py3o/odoo/addons/report_py3o | 1 + setup/report_py3o/setup.py | 6 +++++ 11 files changed, 73 insertions(+), 39 deletions(-) delete mode 100644 report_py3o/views/menu.xml create mode 120000 setup/report_py3o/odoo/addons/report_py3o create mode 100644 setup/report_py3o/setup.py diff --git a/report_py3o/__manifest__.py b/report_py3o/__manifest__.py index 96e04f9c4..186395036 100644 --- a/report_py3o/__manifest__.py +++ b/report_py3o/__manifest__.py @@ -4,10 +4,10 @@ "name": "Py3o Report Engine", "summary": "Reporting engine based on Libreoffice (ODT -> ODT, " "ODT -> PDF, ODT -> DOC, ODT -> DOCX, ODS -> ODS, etc.)", - "version": "15.0.1.0.0", + "version": "16.0.1.0.0", "category": "Reporting", "license": "AGPL-3", - "author": "XCG Consulting," "ACSONE SA/NV," "Odoo Community Association (OCA)", + "author": "XCG Consulting, ACSONE SA/NV, Odoo Community Association (OCA)", "website": "https://github.com/OCA/reporting-engine", "depends": ["web"], "external_dependencies": { @@ -21,7 +21,6 @@ }, "data": [ "security/ir.model.access.csv", - "views/menu.xml", "views/py3o_template.xml", "views/ir_actions_report.xml", "demo/report_py3o.xml", diff --git a/report_py3o/controllers/main.py b/report_py3o/controllers/main.py index 8f28cdd43..05be92036 100644 --- a/report_py3o/controllers/main.py +++ b/report_py3o/controllers/main.py @@ -6,18 +6,17 @@ import mimetypes from werkzeug import exceptions from werkzeug.urls import url_decode -from odoo.http import request, route +from odoo.http import content_disposition, request, route, serialize_exception from odoo.tools import html_escape -from odoo.addons.web.controllers import main -from odoo.addons.web.controllers.main import _serialize_exception, content_disposition +from odoo.addons.web.controllers.report import ReportController -class ReportController(main.ReportController): +class ReportController(ReportController): @route() def report_routes(self, reportname, docids=None, converter=None, **data): if converter != "py3o": - return super(ReportController, self).report_routes( + return super().report_routes( reportname=reportname, docids=docids, converter=converter, **data ) context = dict(request.env.context) @@ -44,7 +43,7 @@ class ReportController(main.ReportController): description="Py3o action report not found for report_name " "%s" % reportname ) - res, filetype = action_py3o_report._render(docids, data) + res, filetype = ir_action._render(reportname, docids, data) filename = action_py3o_report.gen_report_download_filename(docids, data) if not filename.endswith(filetype): filename = "{}.{}".format(filename, filetype) @@ -57,7 +56,7 @@ class ReportController(main.ReportController): return request.make_response(res, headers=http_headers) @route() - def report_download(self, data, context=None): + def report_download(self, data, context=None, token=None): """This function is used by 'qwebactionmanager.js' in order to trigger the download of a py3o/controller report. @@ -68,7 +67,7 @@ class ReportController(main.ReportController): requestcontent = json.loads(data) url, report_type = requestcontent[0], requestcontent[1] if "py3o" not in report_type: - return super(ReportController, self).report_download(data, context) + return super().report_download(data, context=context, token=token) try: reportname = url.split("/report/py3o/")[1].split("?")[0] docids = None @@ -90,6 +89,6 @@ class ReportController(main.ReportController): response.set_cookie("fileToken", context) return response except Exception as e: - se = _serialize_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/demo/report_py3o.xml b/report_py3o/demo/report_py3o.xml index bc3d06ec6..cfc515b6a 100644 --- a/report_py3o/demo/report_py3o.xml +++ b/report_py3o/demo/report_py3o.xml @@ -11,6 +11,9 @@ odt report_py3o demo/res_user.odt + object.name.replace(' ', '_') + '-demo.odt' report diff --git a/report_py3o/models/ir_actions_report.py b/report_py3o/models/ir_actions_report.py index 65955521e..408578877 100644 --- a/report_py3o/models/ir_actions_report.py +++ b/report_py3o/models/ir_actions_report.py @@ -157,16 +157,17 @@ class IrActionsReport(models.Model): [("report_name", "=", report_name), ("report_type", "=", report_type)] ) - def _render_py3o(self, res_ids, data): - self.ensure_one() - if self.report_type != "py3o": + @api.model + def _render_py3o(self, report_ref, res_ids, data=None): + report = self._get_report(report_ref) + if report.report_type != "py3o": raise RuntimeError( "py3o rendition is only available on py3o report.\n" - "(current: '{}', expected 'py3o'".format(self.report_type) + "(current: '{}', expected 'py3o'".format(report.report_type) ) return ( self.env["py3o.report"] - .create({"ir_actions_report_id": self.id}) + .create({"ir_actions_report_id": report.id}) .create_report(res_ids, data) ) diff --git a/report_py3o/models/py3o_report.py b/report_py3o/models/py3o_report.py index e018076ec..3aa2d3480 100644 --- a/report_py3o/models/py3o_report.py +++ b/report_py3o/models/py3o_report.py @@ -16,6 +16,8 @@ from zipfile import ZIP_DEFLATED, ZipFile import pkg_resources from odoo import _, api, fields, models, tools +from odoo.exceptions import AccessError +from odoo.tools.safe_eval import safe_eval, time from ._py3o_parser_context import Py3oParserContext @@ -187,7 +189,9 @@ class Py3oReport(models.TransientModel): def _get_parser_context(self, model_instance, data): report_xml = self.ir_actions_report_id context = Py3oParserContext(self.env).localcontext - context.update(report_xml._get_rendering_context(model_instance.ids, data)) + context.update( + report_xml._get_rendering_context(report_xml, model_instance.ids, data) + ) context["objects"] = model_instance self._extend_parser_context(context, report_xml) return context @@ -199,9 +203,30 @@ class Py3oReport(models.TransientModel): # consumption... # ... but odoo wants the whole data in memory anyways :) buffer = BytesIO(f.read()) - self.ir_actions_report_id._postprocess_pdf_report( - model_instance, buffer + attachment_name = safe_eval( + self.ir_actions_report_id.attachment, + {"object": model_instance, "time": time}, ) + if attachment_name: + attachment_vals = { + "name": attachment_name, + "res_model": self.ir_actions_report_id.model, + "res_id": model_instance.id, + "raw": buffer.getvalue(), + } + try: + attach = self.env["ir.attachment"].create(attachment_vals) + except AccessError: + logger.info( + "Cannot save PDF report %s as attachment", + attachment_vals["name"], + ) + else: + logger.info( + "PDF document %s saved as attachment ID %d", + attachment_vals["name"], + attach.id, + ) return result_path def _create_single_report(self, model_instance, data): diff --git a/report_py3o/tests/test_report_py3o.py b/report_py3o/tests/test_report_py3o.py index 71efe1a3f..6ef02b541 100644 --- a/report_py3o/tests/test_report_py3o.py +++ b/report_py3o/tests/test_report_py3o.py @@ -74,13 +74,13 @@ class TestReportPy3o(TransactionCase): or result ) # test the call the the create method inside our custom parser - self.report._render(self.env.user.ids) + self.report._render(self.report.id, self.env.user.ids) self.assertEqual(call_count, patched_pdf.call_count) # generated files no more exists self.assertFalse(os.path.exists(result)) def test_reports(self): - res = self.report._render(self.env.user.ids) + res = self.report._render(self.report.id, self.env.user.ids) self.assertTrue(res) def test_reports_merge_zip(self): @@ -93,7 +93,7 @@ class TestReportPy3o(TransactionCase): py3o_report.__class__, "_zip_results" ) as patched_zip_results: patched_zip_results.side_effect = _zip_results - content, filetype = self.report._render(users.ids) + content, filetype = self.report._render(self.report.id, users.ids) self.assertEqual(1, patched_zip_results.call_count) self.assertEqual(filetype, "zip") @@ -122,7 +122,7 @@ class TestReportPy3o(TransactionCase): # time we ask the report we received the saved attachment not a newly # generated document created_attachement.datas = base64.b64encode(b"new content") - res = self.report._render(self.env.user.ids) + res = self.report._render(self.report.id, self.env.user.ids) self.assertEqual((b"new content", self.report.py3o_filetype), res) def test_report_post_process(self): @@ -150,24 +150,24 @@ class TestReportPy3o(TransactionCase): "odoo.addons.%s" % self.report.module, tmpl_name ) self.assertTrue(os.path.exists(flbk_filename)) - res = self.report._render(self.env.user.ids) + res = self.report._render(self.report.id, self.env.user.ids) self.assertTrue(res) # The generation fails if the template is not found self.report.module = False with self.assertRaises(TemplateNotFound), self.env.cr.savepoint(): - self.report._render(self.env.user.ids) + self.report._render(self.report.id, self.env.user.ids) # the template can also be provided as an abspath if it's root path # is trusted self.report.py3o_template_fallback = flbk_filename with self.assertRaises(TemplateNotFound): - self.report._render(self.env.user.ids) + self.report._render(self.report.id, self.env.user.ids) with temporary_copy(flbk_filename) as tmp_filename: self.report.py3o_template_fallback = tmp_filename tools.config.misc["report_py3o"] = { "root_tmpl_path": os.path.realpath(os.path.dirname(tmp_filename)) } - res = self.report._render(self.env.user.ids) + res = self.report._render(self.report.id, self.env.user.ids) self.assertTrue(res) # the tempalte can also be provided as a binary field @@ -184,7 +184,7 @@ class TestReportPy3o(TransactionCase): ) self.report.py3o_template_id = py3o_template self.report.py3o_template_fallback = flbk_filename - res = self.report._render(self.env.user.ids) + res = self.report._render(self.report.id, self.env.user.ids) self.assertTrue(res) @tools.misc.mute_logger("odoo.addons.report_py3o.models.py3o_report") @@ -240,7 +240,7 @@ class TestReportPy3o(TransactionCase): self.assertFalse(self.report.lo_bin_path) self.assertFalse(self.report.is_py3o_report_not_available) self.assertFalse(self.report.msg_py3o_report_not_available) - res = self.report._render(self.env.user.ids) + res = self.report._render(self.report.id, self.env.user.ids) self.assertTrue(res) # The report should become unavailable for an non native output format @@ -249,7 +249,7 @@ class TestReportPy3o(TransactionCase): self.assertTrue(self.report.is_py3o_report_not_available) self.assertTrue(self.report.msg_py3o_report_not_available) with self.assertRaises(RuntimeError): - self.report._render(self.env.user.ids) + self.report._render(self.report.id, self.env.user.ids) # if we reset the wrong path, everything should work self.env["ir.config_parameter"].set_param( @@ -260,5 +260,5 @@ class TestReportPy3o(TransactionCase): self.assertFalse(self.report.is_py3o_native_format) self.assertFalse(self.report.is_py3o_report_not_available) self.assertFalse(self.report.msg_py3o_report_not_available) - res = self.report._render(self.env.user.ids) + res = self.report._render(self.report.id, self.env.user.ids) self.assertTrue(res) diff --git a/report_py3o/views/menu.xml b/report_py3o/views/menu.xml deleted file mode 100644 index 87385ecab..000000000 --- a/report_py3o/views/menu.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/report_py3o/views/py3o_template.xml b/report_py3o/views/py3o_template.xml index c13f1104a..e057e77e7 100644 --- a/report_py3o/views/py3o_template.xml +++ b/report_py3o/views/py3o_template.xml @@ -4,7 +4,7 @@ py3o.template.configuration.search.view py3o.template - + @@ -21,7 +21,7 @@ py3o.template.configuration.form.view py3o.template -
+ @@ -47,7 +47,8 @@ diff --git a/requirements.txt b/requirements.txt index 4cdf72bb1..ca809738c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,7 @@ # generated from manifests external_dependencies lxml +py3o.formats +py3o.template +PyPDF2 xlrd xlsxwriter diff --git a/setup/report_py3o/odoo/addons/report_py3o b/setup/report_py3o/odoo/addons/report_py3o new file mode 120000 index 000000000..43f3364e5 --- /dev/null +++ b/setup/report_py3o/odoo/addons/report_py3o @@ -0,0 +1 @@ +../../../../report_py3o \ No newline at end of file diff --git a/setup/report_py3o/setup.py b/setup/report_py3o/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/report_py3o/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)