Skip to content
Snippets Groups Projects
ir_actions_report.py 6.98 KiB
Newer Older
oury.balde's avatar
oury.balde committed
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)