diff --git a/docx_report_generation/__manifest__.py b/docx_report_generation/__manifest__.py index 0584174..8a9533a 100755 --- a/docx_report_generation/__manifest__.py +++ b/docx_report_generation/__manifest__.py @@ -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"], diff --git a/docx_report_generation/models/ir_actions_report.py b/docx_report_generation/models/ir_actions_report.py index 8098c20..7cbee79 100644 --- a/docx_report_generation/models/ir_actions_report.py +++ b/docx_report_generation/models/ir_actions_report.py @@ -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('
', '').replace(' ', ' ') + xml_tree = etree.fromstring('%s' % 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