import logging from odoo import api, fields, models from odoo.tools import pycompat from odoo.addons.mail.models.mail_template import * _logger = logging.getLogger(__name__) class IrActionsReport(models.Model): """ Inherit from ir.action.report to allow customizing the template file. """ _inherit = "ir.actions.report" report_type = fields.Selection(selection_add=[("redner", "redner")]) redner_tmpl_id = fields.Many2one( comodel_name="redner.template", string="Redner template", domain=[("active", "=", True)], ) redner_filetype = fields.Selection( selection=[("html", "html"), ("pdf", "pdf")], string="Output Format" ) substitution_ids = fields.One2many( comodel_name="redner.substitution", inverse_name="ir_actions_report_id", string="Substitution", ) @api.model def render_post_process(self, html): html = self._replace_local_links(html) return html @api.model def render_template( self, template_txt, model, res_ids, post_process=False ): """ Render the given template text, replace mako expressions ``${expr}`` with the result of evaluating these expressions with an evaluation context containing: - ``user``: Model of the current user - ``object``: record of the document record this mail is related to - ``context``: the context passed to the mail composition wizard :param str template_txt: the template text to render :param str model: model name of the document record this mail is related to. :param int res_ids: list of ids of document records those mails are related to. """ multi_mode = True if isinstance(res_ids, pycompat.integer_types): multi_mode = False res_ids = [res_ids] results = dict.fromkeys(res_ids, u"") # try to load the template try: mako_env = ( mako_safe_template_env if self.env.context.get("safe") else mako_template_env ) template = mako_env.from_string(tools.ustr(template_txt)) except Exception: _logger.info( "Failed to load template %r", template_txt, exc_info=True ) return multi_mode and results or results[res_ids[0]] # prepare template variables records = self.env[model].browse( it for it in res_ids if it ) # filter to avoid browsing [None] res_to_rec = dict.fromkeys(res_ids, None) for record in records: res_to_rec[record.id] = record variables = { "format_date": lambda date, format=False, context=self._context: ( format_date(self.env, date, format) ), "format_tz": ( lambda dt, tz=False, format=False, context=self._context: ( format_tz(self.env, dt, tz, format) ) ), "format_amount": lambda amount, currency, context=self._context: ( format_amount(self.env, amount, currency) ), "user": self.env.user, "ctx": self._context, # context kw would clash with mako internals } for res_id, record in res_to_rec.items(): variables["object"] = record try: render_result = template.render(variables) except Exception as e: _logger.info( "Failed to render template %r using values %r" % (template, variables), exc_info=True, ) raise UserError( _("Failed to render template %r using values %r") % (template, variables) + "\n\n%s: %s" % (type(e).__name__, str(e)) ) if render_result == u"False": render_result = u"" results[res_id] = render_result if post_process: for res_id, result in results.items(): results[res_id] = self.render_post_process(result) return multi_mode and results or results[res_ids[0]] @api.multi def action_get_substitutions(self): """ Call by: action button `Get Substitutions from Redner Report` """ self.ensure_one() if self.redner_tmpl_id: keywords = self.redner_tmpl_id.get_keywords() # Get current substitutions subs = self.substitution_ids.mapped("keyword") or [] values = [] for key in keywords: # check to avoid duplicate keys if key not in subs: values.append((0, 0, {"keyword": key})) self.write({"substitution_ids": values}) # remove obsolete keywords in substitutions model if len(self.substitution_ids) > len(keywords): deprecated_keys = self.substitution_ids.filtered( lambda s: s.keyword not in keywords ) if len(deprecated_keys) > 0: deprecated_keys.unlink() def _patch_report_values(self, res_id): # get redner template values import ipdb ipdb.set_trace() values_sent_to_redner = {} for sub in self.substitution_ids: value = self.render_template(sub.value, self.model, res_id) if sub.deserialize: value = ast.literal_eval(value) values_sent_to_redner[sub.keyword] = value try: res = self.redner_tmpl_id.redner.templates.render( self.redner_tmpl_id.redner_id, **values_sent_to_redner ) except Exception as e: if isinstance(e, ValidationError): raise raise ValidationError( _( "We received an unexpected error from redner server. " "Please contact your administrator" ) ) return res def render_redner(self, res_ids, data): self.ensure_one() if self.report_type != "redner": raise RuntimeError( "redner rendition is only available on redner report.\n" "(current: '{}', expected 'redner'".format(self.report_type) ) res = {res_id: self._patch_report_values(res_id) for res_id in res_ids} return True def gen_report_download_filename(self, res_ids, data): """Override this function to change the name of the downloaded report """ self.ensure_one() report = self.get_from_report_name(self.report_name, self.report_type) if report.print_report_name and not len(res_ids) > 1: obj = self.env[self.model].browse(res_ids) return safe_eval( report.print_report_name, {"object": obj, "time": time} ) return "{}.{}".format(self.name, self.redner_filetype)