Skip to content

Commit aa03e0f

Browse files
committed
[ADD] report_playwright
1 parent f90055f commit aa03e0f

File tree

7 files changed

+652
-0
lines changed

7 files changed

+652
-0
lines changed

report_playwright/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import controllers
2+
from . import models

report_playwright/__manifest__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright 2025 Akretion (http://akretion.com)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
{
4+
"name": "Playwright Report Engine",
5+
"summary": "Reporting engine based on playwright",
6+
"version": "18.0.1.0.1",
7+
"category": "Reporting",
8+
"license": "AGPL-3",
9+
"author": "François Poizat (Akretion), Odoo Community Association (OCA)",
10+
"website": "https://github.com/OCA/reporting-engine",
11+
"depends": ["web"],
12+
"external_dependencies": {
13+
"python": ["playwright"],
14+
},
15+
"assets": {
16+
"web.assets_backend": [
17+
"report_playwright/static/src/js/playwright_action_service.esm.js",
18+
],
19+
},
20+
"data": [],
21+
"installable": True,
22+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import playwright
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import json
2+
import logging
3+
4+
from werkzeug.exceptions import InternalServerError
5+
from werkzeug.urls import url_parse
6+
7+
from odoo.http import content_disposition, request, route, serialize_exception
8+
from odoo.tools.misc import html_escape
9+
from odoo.tools.safe_eval import safe_eval, time
10+
11+
from odoo.addons.web.controllers.report import ReportController
12+
13+
_logger = logging.getLogger(__name__)
14+
15+
16+
class ReportController(ReportController):
17+
@route()
18+
def report_routes(self, reportname, docids=None, converter=None, **data):
19+
report = request.env["ir.actions.report"]
20+
context = dict(request.env.context)
21+
22+
if converter != "playwright-pdf":
23+
return super().report_routes(
24+
reportname=reportname, docids=docids, converter=converter, **data
25+
)
26+
27+
if docids:
28+
docids = [int(i) for i in docids.split(",") if i.isdigit()]
29+
if data.get("options"):
30+
data.update(json.loads(data.pop("options")))
31+
if data.get("context"):
32+
data["context"] = json.loads(data["context"])
33+
context.update(data["context"])
34+
35+
pdf = report.with_context(**context)._render_playwright_pdf(
36+
reportname, docids, data=data
37+
)[0]
38+
pdfhttpheaders = [
39+
("Content-Type", "application/pdf"),
40+
("Content-Length", len(pdf)),
41+
]
42+
return request.make_response(pdf, headers=pdfhttpheaders)
43+
44+
@route()
45+
def report_download(
46+
self, data, context=None, token=None
47+
): # pylint: disable=unused-argument
48+
requestcontent = json.loads(data)
49+
url, type_ = requestcontent[0], requestcontent[1]
50+
reportname = "???"
51+
52+
try:
53+
if type_ == "playwright-pdf":
54+
converter = "playwright-pdf"
55+
56+
pattern = "/report/html/"
57+
reportname = url.split(pattern)[1].split("?")[0]
58+
59+
docids = None
60+
if "/" in reportname:
61+
reportname, docids = reportname.split("/")
62+
63+
parsed_url = url_parse(url)
64+
data = parsed_url.decode_query(cls=dict)
65+
data["path"] = parsed_url.path
66+
67+
if docids:
68+
# Generic report:
69+
response = self.report_routes(
70+
reportname, docids=docids, converter=converter, **data
71+
)
72+
else:
73+
# Particular report:
74+
if "context" in data:
75+
context, data_context = json.loads(context or "{}"), json.loads(
76+
data.pop("context")
77+
)
78+
context = json.dumps({**context, **data_context})
79+
response = self.report_routes(
80+
reportname, converter=converter, context=context, **data
81+
)
82+
83+
report = request.env["ir.actions.report"]._get_report_from_name(
84+
reportname
85+
)
86+
filename = "%s.%s" % (report.name, "pdf")
87+
88+
if docids:
89+
ids = [int(x) for x in docids.split(",") if x.isdigit()]
90+
obj = request.env[report.model].browse(ids)
91+
if report.print_report_name and not len(obj) > 1:
92+
report_name = safe_eval(
93+
report.print_report_name, {"object": obj, "time": time}
94+
)
95+
filename = "%s.%s" % (report_name, "pdf")
96+
97+
response.headers.add(
98+
"Content-Disposition", content_disposition(filename)
99+
)
100+
101+
return response
102+
else:
103+
return super().report_download(data, context=context, token=token)
104+
except Exception as e:
105+
_logger.exception("Error while generating report %s", reportname)
106+
se = serialize_exception(e)
107+
error = {"code": 200, "message": "Odoo Server Error", "data": se}
108+
res = request.make_response(html_escape(json.dumps(error)))
109+
raise InternalServerError(response=res) from e
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import ir_actions_report

0 commit comments

Comments
 (0)