etobella
8 years ago
committed by
Enric Tobella
13 changed files with 157 additions and 139 deletions
-
21report_xml/README.rst
-
7report_xml/__init__.py
-
12report_xml/__manifest__.py
-
4report_xml/controllers/__init__.py
-
7report_xml/controllers/main.py
-
21report_xml/demo/report.xml
-
111report_xml/models.py
-
5report_xml/models/__init__.py
-
49report_xml/models/report_action.py
-
25report_xml/models/report_generator.py
-
4report_xml/tests/__init__.py
-
24report_xml/tests/test_report_xml.py
-
6report_xml/views/report_xml_templates.xml
@ -1,4 +1,5 @@ |
|||||
# -*- encoding: utf-8 -*- |
|
||||
# Copyright (C) 2014-2015 Grupo ESOC <www.grupoesoc.es> |
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
from . import controllers, models |
|
||||
|
from . import controllers |
||||
|
from . import models |
@ -0,0 +1,4 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
from . import main |
@ -1,8 +1,9 @@ |
|||||
# -*- encoding: utf-8 -*- |
|
||||
|
# -*- coding: utf-8 -*- |
||||
# Copyright (C) 2014-2015 Grupo ESOC <www.grupoesoc.es> |
# Copyright (C) 2014-2015 Grupo ESOC <www.grupoesoc.es> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
from openerp.http import route |
|
||||
from openerp.addons.report.controllers import main as report |
|
||||
|
from odoo.addons.report.controllers import main as report |
||||
|
from odoo.http import route |
||||
|
|
||||
|
|
||||
class ReportController(report.ReportController): |
class ReportController(report.ReportController): |
@ -0,0 +1,21 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<odoo> |
||||
|
|
||||
|
<template id="demo_report_xml_view"> |
||||
|
<t t-call="report_xml.utf8_header"> |
||||
|
<root> |
||||
|
<user t-foreach="docs" t-as="doc"> |
||||
|
<name t-esc="doc.name"/> |
||||
|
<vat t-esc="doc.vat"/> |
||||
|
</user> |
||||
|
</root> |
||||
|
</t> |
||||
|
</template> |
||||
|
|
||||
|
<report id="demo_xml_report" |
||||
|
name="report_xml.demo_report_xml_view" |
||||
|
string="Demo xml report" |
||||
|
report_type="qweb-xml" |
||||
|
model="res.company"/> |
||||
|
|
||||
|
</odoo> |
@ -1,111 +0,0 @@ |
|||||
# -*- encoding: utf-8 -*- |
|
||||
# Copyright (C) 2014-2015 Grupo ESOC <www.grupoesoc.es> |
|
||||
|
|
||||
import logging |
|
||||
from lxml import etree |
|
||||
from openerp import api, fields, models |
|
||||
|
|
||||
|
|
||||
_logger = logging.getLogger(__name__) |
|
||||
|
|
||||
|
|
||||
class ReportAction(models.Model): |
|
||||
_inherit = "ir.actions.report.xml" |
|
||||
|
|
||||
report_type = fields.Selection(selection_add=[("qweb-xml", "XML")]) |
|
||||
|
|
||||
def _lookup_report(self, cr, name): |
|
||||
"""Enable ``qweb-xml`` report lookup.""" |
|
||||
try: |
|
||||
return super(ReportAction, self)._lookup_report(cr, name) |
|
||||
except Exception as ex: |
|
||||
# Somebody thought it was a good idea to use standard exceptions |
|
||||
if "qweb-xml" not in ex.message: |
|
||||
raise ex |
|
||||
else: |
|
||||
cr.execute( |
|
||||
"SELECT * FROM ir_act_report_xml WHERE report_name=%s", |
|
||||
(name,)) |
|
||||
return cr.dictfetchone()["report_name"] |
|
||||
|
|
||||
@api.model |
|
||||
def render_report(self, res_ids, name, data): |
|
||||
"""Special handling for ``qweb-xml`` reports.""" |
|
||||
if data.get("report_type") == u"qweb-xml": |
|
||||
new_report = self._lookup_report(name) |
|
||||
recs = self.env[self.env.context["active_model"]].browse(res_ids) |
|
||||
result = self.env["report"].get_html(recs, new_report, data=data) |
|
||||
|
|
||||
# XML with spaces before the <?xml tag will fail, and trailing ones |
|
||||
# do nothing, so let's strip them and make everyone happier |
|
||||
result = (result.strip(), "xml") |
|
||||
else: |
|
||||
result = super(ReportAction, self).render_report( |
|
||||
res_ids, name, data) |
|
||||
|
|
||||
return result |
|
||||
|
|
||||
|
|
||||
class ReportGenerator(models.Model): |
|
||||
_inherit = "report" |
|
||||
|
|
||||
@api.model |
|
||||
def _get_report_from_name(self, report_name): |
|
||||
"""Allow to view ``qweb-xml`` reports as web pages.""" |
|
||||
try: |
|
||||
return (super(ReportGenerator, self) |
|
||||
._get_report_from_name(report_name)) |
|
||||
except IndexError: |
|
||||
return self.env["ir.actions.report.xml"].search( |
|
||||
[("report_type", "=", "qweb-xml"), |
|
||||
("report_name", "=", report_name)])[0] |
|
||||
|
|
||||
|
|
||||
class XSDCheckedReport(models.AbstractModel): |
|
||||
"""Check XML report against a XSD schema before downloading it. |
|
||||
|
|
||||
This is an Abstract Model to be inherited by the real report models, which |
|
||||
must implement :meth:`.xsd` and have a ``_name`` in the form |
|
||||
``report.<module>.<report_name>``. |
|
||||
""" |
|
||||
_name = "report_xml.xsd_checked_report" |
|
||||
_description = "Base model for reports that need XSD checking" |
|
||||
|
|
||||
@api.multi |
|
||||
def xsd(self): |
|
||||
"""Return the XSD schema contents.""" |
|
||||
raise NotImplementedError |
|
||||
|
|
||||
@api.multi |
|
||||
def render_html(self, data=None): |
|
||||
"""Return the XML report after checking it against an XSD. |
|
||||
|
|
||||
If ``context`` contains a dict called ``docargs``, it will be used as |
|
||||
the Qweb context. The special key ``docs`` will be added to ``docargs`` |
|
||||
automatically if missing. |
|
||||
""" |
|
||||
# Qweb context |
|
||||
docargs = self.env.context.get("docargs", dict()) |
|
||||
if "docs" not in docargs: |
|
||||
docargs["docs"] = (self.env[self.env.context["active_model"]] |
|
||||
.browse(self.env.context["active_ids"])) |
|
||||
|
|
||||
# Load XSD |
|
||||
xsd = etree.XML(self.xsd()) |
|
||||
_logger.debug("XSD schema contents: %s", etree.tostring(xsd)) |
|
||||
xsd = etree.XMLSchema(xsd) |
|
||||
parser = etree.XMLParser(schema=xsd) |
|
||||
|
|
||||
# Generate XML report |
|
||||
result = (self.env["report"] |
|
||||
.render(self._name[len("report."):], docargs) |
|
||||
.strip()) |
|
||||
|
|
||||
# Validate XML with XSD |
|
||||
try: |
|
||||
etree.fromstring(result, parser) |
|
||||
except Exception as error: |
|
||||
_logger.error(result) |
|
||||
raise error |
|
||||
|
|
||||
return result |
|
@ -0,0 +1,5 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
from . import report_action |
||||
|
from . import report_generator |
@ -0,0 +1,49 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright (C) 2014-2015 Grupo ESOC <www.grupoesoc.es> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
import logging |
||||
|
|
||||
|
from odoo import api, fields, models |
||||
|
from lxml import etree |
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class ReportAction(models.Model): |
||||
|
_inherit = "ir.actions.report.xml" |
||||
|
|
||||
|
report_type = fields.Selection(selection_add=[("qweb-xml", "XML")]) |
||||
|
|
||||
|
def _lookup_report(self, name): |
||||
|
"""Enable ``qweb-xml`` report lookup.""" |
||||
|
try: |
||||
|
return super(ReportAction, self)._lookup_report(name) |
||||
|
except Exception as ex: |
||||
|
# Somebody thought it was a good idea to use standard exceptions |
||||
|
if "qweb-xml" not in ex.message: |
||||
|
raise ex |
||||
|
else: |
||||
|
self._cr.execute( |
||||
|
"SELECT * FROM ir_act_report_xml WHERE report_name=%s", |
||||
|
(name,)) |
||||
|
return self._cr.dictfetchone()["report_name"] |
||||
|
|
||||
|
@api.model |
||||
|
def render_report(self, res_ids, name, data): |
||||
|
"""Special handling for ``qweb-xml`` reports.""" |
||||
|
xml_report = self.search([('report_name', '=', name), |
||||
|
('report_type', '=', 'qweb-xml')], limit=1) |
||||
|
if xml_report: |
||||
|
xml_report = xml_report.ensure_one() |
||||
|
result = self.env["report"].get_html(res_ids, |
||||
|
xml_report.report_name, |
||||
|
data=data) |
||||
|
return ( |
||||
|
etree.tostring( |
||||
|
etree.fromstring(result.strip()), |
||||
|
encoding='UTF-8', xml_declaration=True, pretty_print=True |
||||
|
), "xml") |
||||
|
else: |
||||
|
return super(ReportAction, self).render_report( |
||||
|
res_ids, name, data) |
@ -0,0 +1,25 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright (C) 2014-2015 Grupo ESOC <www.grupoesoc.es> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
import logging |
||||
|
|
||||
|
from odoo import api, models |
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class ReportGenerator(models.Model): |
||||
|
_inherit = "report" |
||||
|
|
||||
|
@api.model |
||||
|
def _get_report_from_name(self, report_name): |
||||
|
res = super(ReportGenerator, self)._get_report_from_name(report_name) |
||||
|
if res: |
||||
|
return res |
||||
|
report_obj = self.env['ir.actions.report.xml'] |
||||
|
qwebtypes = ['qweb-xml'] |
||||
|
conditions = [('report_type', 'in', qwebtypes), |
||||
|
('report_name', '=', report_name)] |
||||
|
context = self.env['res.users'].context_get() |
||||
|
return report_obj.with_context(context).search(conditions, limit=1) |
@ -0,0 +1,4 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
|
from . import test_report_xml |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2017 Creu Blanca |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
|
from lxml import etree |
||||
|
from odoo.tests import common |
||||
|
|
||||
|
|
||||
|
class TestXmlReport(common.TransactionCase): |
||||
|
def test_xml(self): |
||||
|
report_object = self.env['ir.actions.report.xml'] |
||||
|
report_name = 'report_xml.demo_report_xml_view' |
||||
|
self.assertEqual( |
||||
|
report_name, report_object._lookup_report(report_name)) |
||||
|
docs = self.env['res.company'].search([], limit=1) |
||||
|
rep = report_object.render_report( |
||||
|
docs.ids, report_name, {} |
||||
|
) |
||||
|
root = etree.fromstring(rep[0]) |
||||
|
el = root.xpath('/root/user/name') |
||||
|
self.assertEqual( |
||||
|
el[0].text, |
||||
|
docs.ensure_one().name |
||||
|
) |
@ -1,11 +1,9 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<openerp> |
|
||||
<data> |
|
||||
|
<odoo> |
||||
|
|
||||
<template id="utf8_header"> |
<template id="utf8_header"> |
||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||
<t t-raw="0"/> |
<t t-raw="0"/> |
||||
</template> |
</template> |
||||
|
|
||||
</data> |
|
||||
</openerp> |
|
||||
|
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue