[MIG] report_xml: Migration to 10.0
parent
0aec0c09ad
commit
72f23a6b95
|
@ -1,8 +1,10 @@
|
||||||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
:alt: License: AGPL-3
|
:alt: License: AGPL-3
|
||||||
|
|
||||||
Qweb XML Reports
|
===========
|
||||||
================
|
XML Reports
|
||||||
|
===========
|
||||||
|
|
||||||
This module was written to extend the functionality of the reporting engine to
|
This module was written to extend the functionality of the reporting engine to
|
||||||
support XML reports and allow modules to generate them by code or by QWeb
|
support XML reports and allow modules to generate them by code or by QWeb
|
||||||
|
@ -19,11 +21,6 @@ To install this module, you need to:
|
||||||
But this module does nothing for the end user by itself, so if you have it
|
But this module does nothing for the end user by itself, so if you have it
|
||||||
installed it's probably because there is another module that depends on it.
|
installed it's probably because there is another module that depends on it.
|
||||||
|
|
||||||
Configuration
|
|
||||||
=============
|
|
||||||
|
|
||||||
No manual configuration is needed.
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
@ -63,11 +60,6 @@ For further information, please visit:
|
||||||
* https://www.odoo.com/forum/help-1
|
* https://www.odoo.com/forum/help-1
|
||||||
* https://github.com/OCA/reporting-engine
|
* https://github.com/OCA/reporting-engine
|
||||||
|
|
||||||
Known issues / Roadmap
|
|
||||||
======================
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
Credits
|
Credits
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
@ -77,6 +69,7 @@ Contributors
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Jairo Llopis <j.llopis@grupoesoc.es>
|
* Jairo Llopis <j.llopis@grupoesoc.es>
|
||||||
|
* Enric Tobella <etobella@creublanca.es>
|
||||||
|
|
||||||
Maintainer
|
Maintainer
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- 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).
|
||||||
|
|
||||||
from . import controllers, models
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# -*- 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).
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Qweb XML Reports",
|
"name": "XML Reports",
|
||||||
"version": "8.0.1.0.0",
|
"version": "10.0.1.0.0",
|
||||||
"category": "Reporting",
|
"category": "Reporting",
|
||||||
"website": "https://grupoesoc.es",
|
"website": "https://github.com/OCA/reporting-engine",
|
||||||
"author": "Grupo ESOC Ingeniería de Servicios, "
|
"author": "Grupo ESOC Ingeniería de Servicios, "
|
||||||
"Odoo Community Association (OCA)",
|
"Odoo Community Association (OCA)",
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
|
@ -17,5 +18,8 @@
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
"views/report_xml_templates.xml",
|
"views/report_xml_templates.xml",
|
||||||
|
],
|
||||||
|
"demo": [
|
||||||
|
"demo/report.xml",
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -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 odoo.addons.report.controllers import main as report
|
||||||
from openerp.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>
|
<odoo>
|
||||||
<data>
|
|
||||||
|
|
||||||
<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>
|
</odoo>
|
||||||
</openerp>
|
|
||||||
|
|
Loading…
Reference in New Issue