[IMP][FIX]DOCX Report Generation : enhancements
* Fixes for assets ; * Handle HTML fields with basic rendering ; * Add Jinja filters ; * Add JSON Loads for TPL usage (context).
This commit is contained in:
parent
324810c3ff
commit
58cb82cdfa
@ -11,20 +11,20 @@
|
||||
|
||||
This is the beta version, bugs may be present.
|
||||
""",
|
||||
"author": "RYDLAB",
|
||||
"author": "RYDLAB, Yaltik",
|
||||
"website": "https://rydlab.ru",
|
||||
"category": "Technical",
|
||||
"version": "16.0.2.0.0",
|
||||
"version": "16.0.2.1.0",
|
||||
"license": "LGPL-3",
|
||||
"depends": ["base", "web", "custom_report_field", "report_monetary_helpers"],
|
||||
"external_dependencies": {"python": ["docxcompose", "docxtpl", "bs4"]},
|
||||
"external_dependencies": {"python": ["docxcompose", "docxtpl", "beautifulsoup4"]},
|
||||
"data": [
|
||||
"views/ir_actions_report_views.xml",
|
||||
],
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"docx_report/static/src/css/mimetypes.css",
|
||||
"docx_report/static/src/js/action_manager_report.js",
|
||||
"docx_report_generation/static/src/css/mimetypes.css",
|
||||
"docx_report_generation/static/src/js/action_manager_report.js",
|
||||
],
|
||||
},
|
||||
"images": ["static/description/banner.jpg"],
|
||||
|
@ -2,11 +2,20 @@ from base64 import b64decode
|
||||
from bs4 import BeautifulSoup
|
||||
from collections import OrderedDict
|
||||
from io import BytesIO
|
||||
from functools import partial
|
||||
from re import findall
|
||||
from json import loads
|
||||
from logging import getLogger
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from docx import Document
|
||||
from docx.oxml import OxmlElement
|
||||
from docx.oxml.ns import qn
|
||||
from docx.shared import RGBColor
|
||||
from docxcompose.composer import Composer
|
||||
from docxtpl import DocxTemplate
|
||||
import jinja2
|
||||
from requests import codes as codes_request, post as post_request
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
@ -14,6 +23,7 @@ from odoo import _, api, fields, models
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.http import request
|
||||
from odoo.tools.safe_eval import safe_eval, time
|
||||
from odoo.tools.misc import format_date
|
||||
|
||||
try:
|
||||
from odoo.addons.gotenberg.service.utils import (
|
||||
@ -419,24 +429,77 @@ class IrActionsReport(models.Model):
|
||||
)
|
||||
|
||||
record_to_render = values["docs"]
|
||||
# No more HTML / Markup stripping
|
||||
docs = {
|
||||
key: record_to_render[key]
|
||||
for key in record_to_render._fields.keys()
|
||||
if not isinstance(record_to_render[key], fields.Markup)
|
||||
# if not isinstance(record_to_render[key], fields.Markup)
|
||||
}
|
||||
docs.update(
|
||||
{
|
||||
key: self._parse_markup(record_to_render[key])
|
||||
for key in record_to_render._fields.keys()
|
||||
if isinstance(record_to_render[key], fields.Markup)
|
||||
}
|
||||
)
|
||||
values["docs"] = docs
|
||||
|
||||
def _html_generate(field, tpl):
|
||||
|
||||
def _create_list(paragraph, list_type):
|
||||
p = paragraph._p #access to xml paragraph element
|
||||
pPr = p.get_or_add_pPr() #access paragraph properties
|
||||
numPr = OxmlElement('w:numPr') #create number properties element
|
||||
numId = OxmlElement('w:numId') #create numId element - sets bullet type
|
||||
numId.set(qn('w:val'), list_type) #set list type/indentation
|
||||
numPr.append(numId) #add bullet type to number properties list
|
||||
pPr.append(numPr) #add number properties to paragraph
|
||||
|
||||
if isinstance(field, fields.Markup):
|
||||
html_field = str(field).replace('<br>', '').replace(' ', ' ')
|
||||
xml_tree = etree.fromstring('<root>%s</root>' % html_field)
|
||||
md = tpl.new_subdoc()
|
||||
for child in xml_tree.iter():
|
||||
if child.tag in ('p', 'strong', 'em', 's', 'u', 'ul',
|
||||
'ol', 'li', 'font', 'a'):
|
||||
if child.tag == 'p':
|
||||
p = md.add_paragraph(child.text)
|
||||
elif child.tag == 'a':
|
||||
p.add_run(child.text).style = 'Hyperlink'
|
||||
elif child.tag == 'strong':
|
||||
p.add_run(child.text).bold = True
|
||||
p.add_run(' ')
|
||||
elif child.tag == 'em':
|
||||
p.add_run(child.text).italic = True
|
||||
p.add_run(' ')
|
||||
elif child.tag == 'u':
|
||||
p.add_run(child.text).underline = True
|
||||
p.add_run(' ')
|
||||
elif child.tag == 's':
|
||||
p.add_run(child.text).font.strike = True
|
||||
p.add_run(' ')
|
||||
elif child.tag == 'font' and 'style' in child.attrib:
|
||||
if 'color' in child.get('style'):
|
||||
rgb = [int(_v)
|
||||
for _v in findall(r'\d+', child.get('style'))]
|
||||
if len(rgb) == 3:
|
||||
p.add_run(child.text).font.color.rgb = RGBColor(*rgb)
|
||||
else:
|
||||
p.add_run(child.text)
|
||||
elif child.tag == 'ul':
|
||||
list_type = '9' # 9/25/28 : •, 8 : -, 19 : v
|
||||
elif child.tag == 'ol':
|
||||
list_type = '30' # 27 : 1.2 sans indent, 30: 1.2
|
||||
elif child.tag == 'li':
|
||||
lp = md.add_paragraph(child.text, style='List Paragraph')
|
||||
_create_list(lp, list_type)
|
||||
elif child.tag == 'root':
|
||||
continue
|
||||
else: # Not handled, add text only
|
||||
p.add_run(child.text)
|
||||
return md
|
||||
|
||||
jinja_env = jinja2.Environment()
|
||||
|
||||
docx_content = BytesIO()
|
||||
with BytesIO(b64decode(template)) as template_file:
|
||||
doc = DocxTemplate(template_file)
|
||||
doc.render(values)
|
||||
jinja_env.filters['htmlgen'] = partial(_html_generate, tpl=doc)
|
||||
jinja_env.filters['datefmt'] = lambda dt: format_date(self.env, dt)
|
||||
doc.render(values, jinja_env)
|
||||
doc.save(docx_content)
|
||||
docx_content.seek(0)
|
||||
return docx_content
|
||||
@ -476,3 +539,11 @@ class IrActionsReport(models.Model):
|
||||
_logger.exception(e)
|
||||
finally:
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def _get_rendering_context(self, report, docids, data):
|
||||
data = super()._get_rendering_context(report, docids, data)
|
||||
data.update({
|
||||
'jloads': loads,
|
||||
})
|
||||
return data
|
||||
|
Loading…
x
Reference in New Issue
Block a user