diff --git a/report_xml/README.rst b/report_xml/README.rst
index 0a3b88ab..b0969641 100644
--- a/report_xml/README.rst
+++ b/report_xml/README.rst
@@ -13,6 +13,7 @@ Installation
To install this module, you need to:
+* Install lxml_ in Odoo's ``$PYTHONPATH``.
* Install the repository `reporting-engine`_.
Configuration
@@ -80,3 +81,4 @@ To contribute to this module, please visit http://odoo-community.org.
.. _reporting-engine: https://github.com/OCA/reporting-engine
.. _instructions to create reports: https://www.odoo.com/documentation/8.0/reference/reports.html
.. _custom report: https://www.odoo.com/documentation/8.0/reference/reports.html#custom-reports
+.. _lxml: http://lxml.de/
diff --git a/report_xml/models.py b/report_xml/models.py
index 168e4de9..47e1b874 100644
--- a/report_xml/models.py
+++ b/report_xml/models.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+from lxml import etree
from openerp import api, fields, models
@@ -68,3 +69,33 @@ class ReportGenerator(models.Model):
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..``.
+ """
+ _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."""
+ docargs = {"docs": (self.env[self.env.context["active_model"]]
+ .browse(self.env.context["active_ids"]))}
+ xsd = etree.XMLSchema(etree.XML(self.xsd()))
+ parser = etree.XMLParser(schema=xsd)
+ result = (self.env["report"]
+ .render(self._name[len("report."):], docargs)
+ .strip())
+ etree.fromstring(result, parser)
+
+ return result