OCA reporting engine fork for dev and update.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
3.9 KiB

  1. # License AGPL-3.0 or later (https://www.gnuorg/licenses/agpl.html).
  2. from base64 import b64decode
  3. from xml.dom import minidom
  4. from lxml import etree
  5. from odoo import api, models
  6. from odoo.exceptions import ValidationError
  7. class ReportXmlAbstract(models.AbstractModel):
  8. """
  9. Model `report.report_xml.abstract`.
  10. This class provide basic methods for rendering XML report and it's
  11. validation by XSD schema.
  12. """
  13. _name = "report.report_xml.abstract"
  14. _description = "Abstract XML Report"
  15. @api.model
  16. def generate_report(self, ir_report, docids, data=None):
  17. """
  18. Generate and validate XML report. Use incoming `ir_report` settings
  19. to setup encoding and XMl declaration for result `xml`.
  20. Methods:
  21. * `_get_rendering_context` `ir.actions.report` - get report variables.
  22. It will call `_get_report_values` of report's class if it's exist.
  23. * `render_template` of `ir.actions.report` - get report content
  24. * `validate_report` - check result content
  25. Args:
  26. * ir_report(`ir.actions.report`) - report definition instance in Odoo
  27. * docids(list) - IDs of instances for those report will be generated
  28. * data(dict, None) - variables for report rendering
  29. Returns:
  30. * str - result content of report
  31. * str - `"xml"`
  32. Extra Info:
  33. * Default encoding is `UTF-8`
  34. """
  35. # collect variable for rendering environment
  36. if not data:
  37. data = {}
  38. data.setdefault("report_type", "text")
  39. data = ir_report._get_rendering_context(docids, data)
  40. # render template
  41. result_bin = ir_report._render_template(ir_report.report_name, data)
  42. # prettify result content
  43. # normalize indents
  44. parsed_result_bin = minidom.parseString(result_bin)
  45. result = parsed_result_bin.toprettyxml(indent=" " * 4)
  46. # remove empty lines
  47. utf8 = "UTF-8"
  48. result = "\n".join(
  49. line for line in result.splitlines() if line and not line.isspace()
  50. ).encode(utf8)
  51. content = etree.tostring(
  52. etree.fromstring(result),
  53. encoding=ir_report.xml_encoding or utf8,
  54. xml_declaration=ir_report.xml_declaration,
  55. pretty_print=True,
  56. )
  57. # validate content
  58. xsd_schema_doc = ir_report.xsd_schema
  59. self.validate_report(xsd_schema_doc, content)
  60. return content, "xml"
  61. @api.model
  62. def validate_report(self, xsd_schema_doc, content):
  63. """
  64. Validate final report content against value of `xsd_schema` field
  65. ("XSD Validation Schema") of `ir.actions.report` via `etree` lib.
  66. Args:
  67. * xsd_schema_doc(byte-string) - report validation schema
  68. * content(str) - report content for validation
  69. Raises:
  70. * odoo.exceptions.ValidationError - Syntax of final report is wrong
  71. Returns:
  72. * bool - True
  73. """
  74. if xsd_schema_doc:
  75. # create validation parser
  76. decoded_xsd_schema_doc = b64decode(xsd_schema_doc)
  77. parsed_xsd_schema = etree.XML(decoded_xsd_schema_doc)
  78. xsd_schema = etree.XMLSchema(parsed_xsd_schema)
  79. parser = etree.XMLParser(schema=xsd_schema)
  80. try:
  81. # check content
  82. etree.fromstring(content, parser)
  83. except etree.XMLSyntaxError as error:
  84. raise ValidationError(error.msg)
  85. return True
  86. @api.model
  87. def _get_report_values(self, docids, data=None):
  88. """
  89. Allow to generate extra variables for report environment.
  90. Args:
  91. * docids(list) - IDs of instances for those report will be generated
  92. * data(dict, None) - variables for report rendering
  93. Returns:
  94. * dict - extra variables for report render
  95. """
  96. if not data:
  97. data = {}
  98. return data