mirror of
https://github.com/OCA/reporting-engine.git
synced 2025-02-16 16:30:38 +02:00
[MIG] report_py3o, report_py3o_fusion_server: Migration to 12.0
This commit is contained in:
committed by
Elmeri Niemelä
parent
80d8afb408
commit
e65b6db22f
@@ -1,4 +1,3 @@
|
||||
from . import ir_actions_report_xml
|
||||
from . import py3o_template
|
||||
from . import report
|
||||
from . import ir_actions_report
|
||||
from . import py3o_report
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2013 XCG Consulting (http://odoo.consulting)
|
||||
# Copyright 2018 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
import logging
|
||||
import time
|
||||
@@ -15,13 +15,13 @@ except ImportError:
|
||||
logger.debug('Cannot import py3o.formats')
|
||||
|
||||
|
||||
class IrActionsReportXml(models.Model):
|
||||
""" Inherit from ir.actions.report.xml to allow customizing the template
|
||||
class IrActionsReport(models.Model):
|
||||
""" Inherit from ir.actions.report to allow customizing the template
|
||||
file. The user cam chose a template from a list.
|
||||
The list is configurable in the configuration tab, see py3o_template.py
|
||||
"""
|
||||
|
||||
_inherit = 'ir.actions.report.xml'
|
||||
_inherit = 'ir.actions.report'
|
||||
|
||||
@api.multi
|
||||
@api.constrains("py3o_filetype", "report_type")
|
||||
@@ -43,6 +43,9 @@ class IrActionsReportXml(models.Model):
|
||||
selections.append((name, description))
|
||||
return selections
|
||||
|
||||
report_type = fields.Selection(
|
||||
selection_add=[("py3o", "py3o")]
|
||||
)
|
||||
py3o_filetype = fields.Selection(
|
||||
selection="_get_py3o_filetypes",
|
||||
string="Output Format")
|
||||
@@ -78,12 +81,21 @@ class IrActionsReportXml(models.Model):
|
||||
def render_report(self, res_ids, name, data):
|
||||
action_py3o_report = self.get_from_report_name(name, "py3o")
|
||||
if action_py3o_report:
|
||||
return self.env['py3o.report'].create({
|
||||
'ir_actions_report_xml_id': action_py3o_report.id
|
||||
}).create_report(res_ids, data)
|
||||
return super(IrActionsReportXml, self).render_report(
|
||||
return action_py3o_report._render_py3o(res_ids, data)
|
||||
return super(IrActionsReport, self).render_report(
|
||||
res_ids, name, data)
|
||||
|
||||
@api.multi
|
||||
def _render_py3o(self, res_ids, data):
|
||||
self.ensure_one()
|
||||
if self.report_type != "py3o":
|
||||
raise RuntimeError(
|
||||
"py3o rendition is only available on py3o report.\n"
|
||||
"(current: '{}', expected 'py3o'".format(self.report_type))
|
||||
return self.env['py3o.report'].create({
|
||||
'ir_actions_report_id': self.id
|
||||
}).create_report(res_ids, data)
|
||||
|
||||
@api.multi
|
||||
def gen_report_download_filename(self, res_ids, data):
|
||||
"""Override this function to change the name of the downloaded report
|
||||
@@ -95,3 +107,34 @@ class IrActionsReportXml(models.Model):
|
||||
return safe_eval(report.print_report_name,
|
||||
{'object': obj, 'time': time})
|
||||
return "%s.%s" % (self.name, self.py3o_filetype)
|
||||
|
||||
@api.model
|
||||
def _get_report_from_name(self, report_name):
|
||||
"""Get the first record of ir.actions.report having the
|
||||
``report_name`` as value for the field report_name.
|
||||
"""
|
||||
res = super(IrActionsReport, self)._get_report_from_name(report_name)
|
||||
if res:
|
||||
return res
|
||||
# maybe a py3o report
|
||||
context = self.env['res.users'].context_get()
|
||||
return self.with_context(context).search(
|
||||
[('report_type', '=', 'py3o'),
|
||||
('report_name', '=', report_name)], limit=1)
|
||||
|
||||
@api.multi
|
||||
def _get_attachments(self, res_ids):
|
||||
""" Return the report already generated for the given res_ids
|
||||
"""
|
||||
self.ensure_one()
|
||||
save_in_attachment = {}
|
||||
if res_ids:
|
||||
# Dispatch the records by ones having an attachment
|
||||
Model = self.env[self.model]
|
||||
record_ids = Model.browse(res_ids)
|
||||
if self.attachment:
|
||||
for record_id in record_ids:
|
||||
attachment_id = self.retrieve_attachment(record_id)
|
||||
if attachment_id:
|
||||
save_in_attachment[record_id.id] = attachment_id
|
||||
return save_in_attachment
|
||||
@@ -1,10 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2013 XCG Consulting (http://odoo.consulting)
|
||||
# Copyright 2016 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||
import base64
|
||||
from base64 import b64decode
|
||||
from cStringIO import StringIO
|
||||
from io import BytesIO
|
||||
import logging
|
||||
import os
|
||||
import cgi
|
||||
@@ -16,8 +15,6 @@ import sys
|
||||
import tempfile
|
||||
from zipfile import ZipFile, ZIP_DEFLATED
|
||||
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.report.report_sxw import rml_parse
|
||||
from odoo import api, fields, models, tools, _
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -71,7 +68,7 @@ def format_multiline_value(value):
|
||||
|
||||
|
||||
@py3o_report_extender()
|
||||
def defautl_extend(report_xml, localcontext):
|
||||
def default_extend(report_xml, localcontext):
|
||||
# add the base64decode function to be able do decode binary fields into
|
||||
# the template
|
||||
localcontext['b64decode'] = b64decode
|
||||
@@ -82,11 +79,10 @@ def defautl_extend(report_xml, localcontext):
|
||||
|
||||
class Py3oReport(models.TransientModel):
|
||||
_name = "py3o.report"
|
||||
_inherit = 'report'
|
||||
_description = "Report Py30"
|
||||
|
||||
ir_actions_report_xml_id = fields.Many2one(
|
||||
comodel_name="ir.actions.report.xml",
|
||||
ir_actions_report_id = fields.Many2one(
|
||||
comodel_name="ir.actions.report",
|
||||
required=True
|
||||
)
|
||||
|
||||
@@ -134,7 +130,7 @@ class Py3oReport(models.TransientModel):
|
||||
"""
|
||||
if not tmpl_name:
|
||||
return None
|
||||
report_xml = self.ir_actions_report_xml_id
|
||||
report_xml = self.ir_actions_report_id
|
||||
flbk_filename = None
|
||||
if report_xml.module:
|
||||
# if the default is defined
|
||||
@@ -145,7 +141,7 @@ class Py3oReport(models.TransientModel):
|
||||
elif self._is_valid_template_path(tmpl_name):
|
||||
flbk_filename = os.path.realpath(tmpl_name)
|
||||
if self._is_valid_template_filename(flbk_filename):
|
||||
with open(flbk_filename, 'r') as tmpl:
|
||||
with open(flbk_filename, 'rb') as tmpl:
|
||||
return tmpl.read()
|
||||
return None
|
||||
|
||||
@@ -156,7 +152,7 @@ class Py3oReport(models.TransientModel):
|
||||
:return:
|
||||
"""
|
||||
self.ensure_one()
|
||||
report_xml = self.ir_actions_report_xml_id
|
||||
report_xml = self.ir_actions_report_id
|
||||
return self._get_template_from_path(report_xml.py3o_template_fallback)
|
||||
|
||||
@api.multi
|
||||
@@ -174,7 +170,7 @@ class Py3oReport(models.TransientModel):
|
||||
odoo.exceptions.DeferredException
|
||||
"""
|
||||
self.ensure_one()
|
||||
report_xml = self.ir_actions_report_xml_id
|
||||
report_xml = self.ir_actions_report_id
|
||||
if report_xml.py3o_template_id.py3o_template_data:
|
||||
# if a user gave a report template
|
||||
tmpl_data = b64decode(
|
||||
@@ -187,7 +183,7 @@ class Py3oReport(models.TransientModel):
|
||||
if tmpl_data is None:
|
||||
# if for any reason the template is not found
|
||||
raise TemplateNotFound(
|
||||
_(u'No template found. Aborting.'),
|
||||
_('No template found. Aborting.'),
|
||||
sys.exc_info(),
|
||||
)
|
||||
|
||||
@@ -197,47 +193,35 @@ class Py3oReport(models.TransientModel):
|
||||
def _extend_parser_context(self, context_instance, report_xml):
|
||||
# add default extenders
|
||||
for fct in _extender_functions.get(None, []):
|
||||
fct(report_xml, context_instance.localcontext)
|
||||
fct(report_xml, context_instance)
|
||||
# add extenders for registered on the template
|
||||
xml_id = report_xml.get_external_id().get(report_xml.id)
|
||||
if xml_id in _extender_functions:
|
||||
for fct in _extender_functions[xml_id]:
|
||||
fct(report_xml, context_instance.localcontext)
|
||||
fct(report_xml, context_instance)
|
||||
|
||||
@api.multi
|
||||
def _get_parser_context(self, model_instance, data):
|
||||
report_xml = self.ir_actions_report_xml_id
|
||||
context_instance = rml_parse(self.env.cr, self.env.uid,
|
||||
report_xml.name,
|
||||
context=self.env.context)
|
||||
context_instance.set_context(model_instance, data, model_instance.ids,
|
||||
report_xml.report_type)
|
||||
self._extend_parser_context(context_instance, report_xml)
|
||||
return context_instance.localcontext
|
||||
|
||||
@api.model
|
||||
def _postprocess_report(self, report_path, res_id, save_in_attachment):
|
||||
if save_in_attachment.get(res_id):
|
||||
with open(report_path, 'rb') as pdfreport:
|
||||
attachment = {
|
||||
'name': save_in_attachment.get(res_id),
|
||||
'datas': base64.encodestring(pdfreport.read()),
|
||||
'datas_fname': save_in_attachment.get(res_id),
|
||||
'res_model': save_in_attachment.get('model'),
|
||||
'res_id': res_id,
|
||||
}
|
||||
try:
|
||||
self.env['ir.attachment'].create(attachment)
|
||||
except AccessError:
|
||||
logger.info("Cannot save PDF report %r as attachment",
|
||||
attachment['name'])
|
||||
else:
|
||||
logger.info(
|
||||
'The PDF document %s is now saved in the database',
|
||||
attachment['name'])
|
||||
report_xml = self.ir_actions_report_id
|
||||
context = report_xml._get_rendering_context(model_instance.ids, data)
|
||||
context['objects'] = model_instance
|
||||
self._extend_parser_context(context, report_xml)
|
||||
return context
|
||||
|
||||
@api.multi
|
||||
def _create_single_report(self, model_instance, data, save_in_attachment):
|
||||
def _postprocess_report(self, model_instance, result_path):
|
||||
if len(model_instance) == 1 and self.ir_actions_report_id.attachment:
|
||||
with open(result_path, 'rb') as f:
|
||||
# we do all the generation process using files to avoid memory
|
||||
# consumption...
|
||||
# ... but odoo wants the whole data in memory anyways :)
|
||||
buffer = BytesIO(f.read())
|
||||
self.ir_actions_report_id.postprocess_pdf_report(
|
||||
model_instance, buffer)
|
||||
return result_path
|
||||
|
||||
@api.multi
|
||||
def _create_single_report(self, model_instance, data):
|
||||
""" This function to generate our py3o report
|
||||
"""
|
||||
self.ensure_one()
|
||||
@@ -245,8 +229,8 @@ class Py3oReport(models.TransientModel):
|
||||
suffix='.ods', prefix='p3o.report.tmp.')
|
||||
tmpl_data = self.get_template(model_instance)
|
||||
|
||||
in_stream = StringIO(tmpl_data)
|
||||
with closing(os.fdopen(result_fd, 'w+')) as out_stream:
|
||||
in_stream = BytesIO(tmpl_data)
|
||||
with closing(os.fdopen(result_fd, 'wb+')) as out_stream:
|
||||
template = Template(in_stream, out_stream, escape_false=True)
|
||||
localcontext = self._get_parser_context(model_instance, data)
|
||||
template.render(localcontext)
|
||||
@@ -260,16 +244,12 @@ class Py3oReport(models.TransientModel):
|
||||
result_path, model_instance, data
|
||||
)
|
||||
|
||||
if len(model_instance) == 1:
|
||||
self._postprocess_report(
|
||||
result_path, model_instance.id, save_in_attachment)
|
||||
|
||||
return result_path
|
||||
return self._postprocess_report(model_instance, result_path)
|
||||
|
||||
@api.multi
|
||||
def _convert_single_report(self, result_path, model_instance, data):
|
||||
"""Run a command to convert to our target format"""
|
||||
filetype = self.ir_actions_report_xml_id.py3o_filetype
|
||||
filetype = self.ir_actions_report_id.py3o_filetype
|
||||
if not Formats().get_format(filetype).native:
|
||||
command = self._convert_single_report_cmd(
|
||||
result_path, model_instance, data,
|
||||
@@ -297,30 +277,30 @@ class Py3oReport(models.TransientModel):
|
||||
),
|
||||
'--headless',
|
||||
'--convert-to',
|
||||
self.ir_actions_report_xml_id.py3o_filetype,
|
||||
self.ir_actions_report_id.py3o_filetype,
|
||||
result_path,
|
||||
]
|
||||
|
||||
@api.multi
|
||||
def _get_or_create_single_report(self, model_instance, data,
|
||||
save_in_attachment):
|
||||
existing_reports_attachment):
|
||||
self.ensure_one()
|
||||
if save_in_attachment and save_in_attachment[
|
||||
'loaded_documents'].get(model_instance.id):
|
||||
d = save_in_attachment[
|
||||
'loaded_documents'].get(model_instance.id)
|
||||
attachment = existing_reports_attachment.get(
|
||||
model_instance.id)
|
||||
if attachment and self.ir_actions_report_id.attachment_use:
|
||||
content = base64.decodestring(attachment.datas)
|
||||
report_file = tempfile.mktemp(
|
||||
"." + self.ir_actions_report_xml_id.py3o_filetype)
|
||||
"." + self.ir_actions_report_id.py3o_filetype)
|
||||
with open(report_file, "wb") as f:
|
||||
f.write(d)
|
||||
f.write(content)
|
||||
return report_file
|
||||
return self._create_single_report(
|
||||
model_instance, data, save_in_attachment)
|
||||
model_instance, data)
|
||||
|
||||
@api.multi
|
||||
def _zip_results(self, reports_path):
|
||||
self.ensure_one()
|
||||
zfname_prefix = self.ir_actions_report_xml_id.name
|
||||
zfname_prefix = self.ir_actions_report_id.name
|
||||
result_path = tempfile.mktemp(suffix="zip", prefix='py3o-zip-result')
|
||||
with ZipFile(result_path, 'w', ZIP_DEFLATED) as zf:
|
||||
cpt = 0
|
||||
@@ -335,7 +315,7 @@ class Py3oReport(models.TransientModel):
|
||||
@api.multi
|
||||
def _merge_results(self, reports_path):
|
||||
self.ensure_one()
|
||||
filetype = self.ir_actions_report_xml_id.py3o_filetype
|
||||
filetype = self.ir_actions_report_id.py3o_filetype
|
||||
if not reports_path:
|
||||
return False, False
|
||||
if len(reports_path) == 1:
|
||||
@@ -359,22 +339,22 @@ class Py3oReport(models.TransientModel):
|
||||
def create_report(self, res_ids, data):
|
||||
""" Override this function to handle our py3o report
|
||||
"""
|
||||
model_instances = self.env[self.ir_actions_report_xml_id.model].browse(
|
||||
model_instances = self.env[self.ir_actions_report_id.model].browse(
|
||||
res_ids)
|
||||
save_in_attachment = self._check_attachment_use(
|
||||
res_ids, self.ir_actions_report_xml_id) or {}
|
||||
reports_path = []
|
||||
if (
|
||||
len(res_ids) > 1 and
|
||||
self.ir_actions_report_xml_id.py3o_multi_in_one):
|
||||
self.ir_actions_report_id.py3o_multi_in_one):
|
||||
reports_path.append(
|
||||
self._create_single_report(
|
||||
model_instances, data, save_in_attachment))
|
||||
model_instances, data))
|
||||
else:
|
||||
existing_reports_attachment = \
|
||||
self.ir_actions_report_id._get_attachments(res_ids)
|
||||
for model_instance in model_instances:
|
||||
reports_path.append(
|
||||
self._get_or_create_single_report(
|
||||
model_instance, data, save_in_attachment))
|
||||
model_instance, data, existing_reports_attachment))
|
||||
|
||||
result_path, filetype = self._merge_results(reports_path)
|
||||
reports_path.append(result_path)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2013 XCG Consulting (http://odoo.consulting)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
@@ -6,6 +5,7 @@ from odoo import fields, models
|
||||
|
||||
class Py3oTemplate(models.Model):
|
||||
_name = 'py3o.template'
|
||||
_description = 'Py3o template'
|
||||
|
||||
name = fields.Char(required=True)
|
||||
py3o_template_data = fields.Binary("LibreOffice Template")
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Akretion (http://www.akretion.com/)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class Report(models.Model):
|
||||
|
||||
_inherit = 'report'
|
||||
|
||||
@api.model
|
||||
def _get_report_from_name(self, report_name):
|
||||
"""Get the first record of ir.actions.report.xml having the
|
||||
``report_name`` as value for the field report_name.
|
||||
"""
|
||||
res = super(Report, self)._get_report_from_name(report_name)
|
||||
if res:
|
||||
return res
|
||||
# maybe a py3o report
|
||||
report_obj = self.env['ir.actions.report.xml']
|
||||
context = self.env['res.users'].context_get()
|
||||
return report_obj.with_context(context).search(
|
||||
[('report_type', '=', 'py3o'),
|
||||
('report_name', '=', report_name)], limit=1)
|
||||
Reference in New Issue
Block a user