porting to 16 version finished
This commit is contained in:
parent
da4f636fde
commit
2ba48666f3
@ -10,7 +10,7 @@ from odoo.http import (
|
|||||||
from odoo.tools import html_escape
|
from odoo.tools import html_escape
|
||||||
from odoo.tools.safe_eval import safe_eval, time
|
from odoo.tools.safe_eval import safe_eval, time
|
||||||
|
|
||||||
from odoo.addons.web.controllers.main import ReportController
|
from odoo.addons.web.controllers.report import ReportController
|
||||||
|
|
||||||
|
|
||||||
class DocxReportController(ReportController):
|
class DocxReportController(ReportController):
|
||||||
@ -52,8 +52,8 @@ class DocxReportController(ReportController):
|
|||||||
]
|
]
|
||||||
return request.make_response(pdf, headers=pdfhttpheaders)
|
return request.make_response(pdf, headers=pdfhttpheaders)
|
||||||
else:
|
else:
|
||||||
return super().report_routes(
|
return super(DocxReportController, self).report_routes(
|
||||||
reportname, docids=docids, converter=converter, **data
|
reportname, docids, converter, **data
|
||||||
)
|
)
|
||||||
|
|
||||||
@route()
|
@route()
|
||||||
|
70
i18n/docx_report.pot
Normal file
70
i18n/docx_report.pot
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * docx_report
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 16.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2022-12-29 08:24+0000\n"
|
||||||
|
"PO-Revision-Date: 2022-12-29 08:24+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields.selection,name:docx_report.selection__ir_actions_report__report_type__docx-docx
|
||||||
|
msgid "DOCX"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields.selection,name:docx_report.selection__ir_actions_report__report_type__docx-pdf
|
||||||
|
msgid "DOCX(PDF)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/docx_report/models/ir_actions_report.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Gotenberg converting service not available. The PDF can not be created."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/docx_report/models/ir_actions_report.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "One of the documents you try to merge caused failure."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model,name:docx_report.model_ir_actions_report
|
||||||
|
msgid "Report Action"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields,field_description:docx_report.field_ir_actions_report__report_type
|
||||||
|
msgid "Report Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields,field_description:docx_report.field_ir_actions_report__report_docx_template
|
||||||
|
msgid "Report docx template"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields,field_description:docx_report.field_ir_actions_report__report_name
|
||||||
|
msgid "Template Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields,help:docx_report.field_ir_actions_report__report_type
|
||||||
|
msgid ""
|
||||||
|
"The type of the report that will be rendered, each one having its own "
|
||||||
|
"rendering method. HTML means the report will be opened directly in your "
|
||||||
|
"browser PDF means the report will be rendered using Wkhtmltopdf and "
|
||||||
|
"downloaded by the user."
|
||||||
|
msgstr ""
|
75
i18n/ru.po
Normal file
75
i18n/ru.po
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * docx_report
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 16.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2022-12-29 08:24+0000\n"
|
||||||
|
"PO-Revision-Date: 2022-12-29 08:24+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields.selection,name:docx_report.selection__ir_actions_report__report_type__docx-docx
|
||||||
|
msgid "DOCX"
|
||||||
|
msgstr "DOCX"
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields.selection,name:docx_report.selection__ir_actions_report__report_type__docx-pdf
|
||||||
|
msgid "DOCX(PDF)"
|
||||||
|
msgstr "DOCX(PDF)"
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/docx_report/models/ir_actions_report.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Gotenberg converting service not available. The PDF can not be created."
|
||||||
|
msgstr ""
|
||||||
|
"Файл PDF не может быть создан, так как сервис конвертации Gotenberg не доступен."
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/docx_report/models/ir_actions_report.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "One of the documents you try to merge caused failure."
|
||||||
|
msgstr "Один из документов, которые вы пытаетесь соединить, вызывает ошибку."
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model,name:docx_report.model_ir_actions_report
|
||||||
|
msgid "Report Action"
|
||||||
|
msgstr "Действие для отчета."
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields,field_description:docx_report.field_ir_actions_report__report_type
|
||||||
|
msgid "Report Type"
|
||||||
|
msgstr "Тип отчета"
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields,field_description:docx_report.field_ir_actions_report__report_docx_template
|
||||||
|
msgid "Report docx template"
|
||||||
|
msgstr "Шаблон для отчета docx"
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields,field_description:docx_report.field_ir_actions_report__report_name
|
||||||
|
msgid "Template Name"
|
||||||
|
msgstr "Имя шаблона"
|
||||||
|
|
||||||
|
#. module: docx_report
|
||||||
|
#: model:ir.model.fields,help:docx_report.field_ir_actions_report__report_type
|
||||||
|
msgid ""
|
||||||
|
"The type of the report that will be rendered, each one having its own "
|
||||||
|
"rendering method. HTML means the report will be opened directly in your "
|
||||||
|
"browser PDF means the report will be rendered using Wkhtmltopdf and "
|
||||||
|
"downloaded by the user."
|
||||||
|
msgstr ""
|
||||||
|
"Тип генерируемого отчета. Каждый тип имеет свой собственный"
|
||||||
|
" метод генерации. HTML означает, что отчет будет открыт непосредственно в"
|
||||||
|
" вашем браузере, PDF означает, что отчет будет сгенерирован с помощью "
|
||||||
|
"Wkhtmltopdf и загружен пользователем."
|
@ -64,7 +64,7 @@ class IrActionsReport(models.Model):
|
|||||||
|
|
||||||
def retrieve_attachment(self, record):
|
def retrieve_attachment(self, record):
|
||||||
"""
|
"""
|
||||||
Поиск существующего файла отчета во вложениях записи по:
|
Searc for existing report file in record's attachments by fields:
|
||||||
1. name
|
1. name
|
||||||
2. res_model
|
2. res_model
|
||||||
3. res_id
|
3. res_id
|
||||||
@ -88,8 +88,8 @@ class IrActionsReport(models.Model):
|
|||||||
@api.model
|
@api.model
|
||||||
def _render_docx_pdf(self, res_ids=None, data=None):
|
def _render_docx_pdf(self, res_ids=None, data=None):
|
||||||
"""
|
"""
|
||||||
Подготавливает данные для рендера файла отчета, вызывает метод рендера
|
Prepares the data for report file rendering, calls for the render method
|
||||||
И обрабатывает результат рендера
|
and handle rendering result.
|
||||||
"""
|
"""
|
||||||
if not data:
|
if not data:
|
||||||
data = {}
|
data = {}
|
||||||
@ -100,7 +100,7 @@ class IrActionsReport(models.Model):
|
|||||||
|
|
||||||
save_in_attachment = OrderedDict()
|
save_in_attachment = OrderedDict()
|
||||||
# Maps the streams in `save_in_attachment` back to the records they came from
|
# Maps the streams in `save_in_attachment` back to the records they came from
|
||||||
stream_record = dict()
|
# stream_record = dict()
|
||||||
if res_ids:
|
if res_ids:
|
||||||
Model = self.env[self_sudo.model]
|
Model = self.env[self_sudo.model]
|
||||||
record_ids = Model.browse(res_ids)
|
record_ids = Model.browse(res_ids)
|
||||||
@ -108,19 +108,20 @@ class IrActionsReport(models.Model):
|
|||||||
if self_sudo.attachment:
|
if self_sudo.attachment:
|
||||||
for record_id in record_ids:
|
for record_id in record_ids:
|
||||||
attachment = self_sudo.retrieve_attachment(record_id)
|
attachment = self_sudo.retrieve_attachment(record_id)
|
||||||
if attachment:
|
if attachment and self_sudo.attachment_use:
|
||||||
stream = self_sudo._retrieve_stream_from_attachment(attachment)
|
# stream = self_sudo._retrieve_stream_from_attachment(attachment)
|
||||||
|
stream = BytesIO(attachment.raw)
|
||||||
save_in_attachment[record_id.id] = stream
|
save_in_attachment[record_id.id] = stream
|
||||||
stream_record[stream] = record_id
|
# stream_record[stream] = record_id
|
||||||
if not self_sudo.attachment_use or not attachment:
|
if not self_sudo.attachment_use or not attachment:
|
||||||
docx_record_ids += record_id
|
docx_record_ids += record_id
|
||||||
else:
|
else:
|
||||||
docx_record_ids = record_ids
|
docx_record_ids = record_ids
|
||||||
res_ids = docx_record_ids.ids
|
res_ids = docx_record_ids.ids
|
||||||
|
|
||||||
if save_in_attachment and not res_ids:
|
if save_in_attachment: # and not res_ids:
|
||||||
_logger.info("The PDF report has been generated from attachment.")
|
_logger.info("The PDF report has been generated from attachment.")
|
||||||
self._raise_on_unreadable_pdfs(save_in_attachment.values(), stream_record)
|
# self._raise_on_unreadable_pdfs(save_in_attachment.values(), stream_record)
|
||||||
return self_sudo._post_pdf(save_in_attachment), "pdf"
|
return self_sudo._post_pdf(save_in_attachment), "pdf"
|
||||||
|
|
||||||
docx_content = self._render_docx(res_ids, data=data)
|
docx_content = self._render_docx(res_ids, data=data)
|
||||||
@ -139,24 +140,22 @@ class IrActionsReport(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if res_ids:
|
if res_ids:
|
||||||
self._raise_on_unreadable_pdfs(save_in_attachment.values(), stream_record)
|
# self._raise_on_unreadable_pdfs(save_in_attachment.values(), stream_record)
|
||||||
_logger.info(
|
# saving pdf in attachment.
|
||||||
"The PDF report has been generated for model: %s, records %s."
|
|
||||||
% (self_sudo.model, str(res_ids))
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
self_sudo._post_pdf(
|
self_sudo._post_pdf(
|
||||||
save_in_attachment, pdf_content=pdf_content, res_ids=res_ids
|
save_in_attachment, pdf_content=pdf_content, res_ids=res_ids
|
||||||
),
|
),
|
||||||
"pdf",
|
"pdf",
|
||||||
)
|
)
|
||||||
|
|
||||||
return pdf_content, "pdf"
|
return pdf_content, "pdf"
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _render_docx_docx(self, res_ids=None, data=None):
|
def _render_docx_docx(self, res_ids=None, data=None):
|
||||||
"""
|
"""
|
||||||
Подготавливает данные для рендера файла отчета, вызывает метод рендера
|
Prepares the data for report file rendering, calls for the render method
|
||||||
И обрабатывает результат рендера
|
and handle rendering result.
|
||||||
"""
|
"""
|
||||||
if not data:
|
if not data:
|
||||||
data = {}
|
data = {}
|
||||||
@ -176,7 +175,8 @@ class IrActionsReport(models.Model):
|
|||||||
for record_id in record_ids:
|
for record_id in record_ids:
|
||||||
attachment = self_sudo.retrieve_attachment(record_id)
|
attachment = self_sudo.retrieve_attachment(record_id)
|
||||||
if attachment:
|
if attachment:
|
||||||
stream = self_sudo._retrieve_stream_from_attachment(attachment)
|
# stream = self_sudo._retrieve_stream_from_attachment(attachment)
|
||||||
|
stream = BytesIO(attachment.raw)
|
||||||
save_in_attachment[record_id.id] = stream
|
save_in_attachment[record_id.id] = stream
|
||||||
stream_record[stream] = record_id
|
stream_record[stream] = record_id
|
||||||
if not self_sudo.attachment_use or not attachment:
|
if not self_sudo.attachment_use or not attachment:
|
||||||
@ -204,11 +204,52 @@ class IrActionsReport(models.Model):
|
|||||||
)
|
)
|
||||||
return docx_content, "docx"
|
return docx_content, "docx"
|
||||||
|
|
||||||
|
def _post_pdf(self, save_in_attachment, pdf_content=None, res_ids=None):
|
||||||
|
"""
|
||||||
|
Adds pdf file in record's attachments.
|
||||||
|
TODO: For now bunch generation is not supported.
|
||||||
|
2 execution ways:
|
||||||
|
- save_in_attachment and not res_ids - when get reports from attachments
|
||||||
|
- res_ids and not save_in_attachment - when generate report.
|
||||||
|
"""
|
||||||
|
self_sudo = self.sudo()
|
||||||
|
attachment_vals_list = []
|
||||||
|
if save_in_attachment:
|
||||||
|
# here get streams from save_in_attachment, make pdf file and return it
|
||||||
|
# bunch generation here is already realized.
|
||||||
|
reports_data = list(save_in_attachment.values())
|
||||||
|
if len(reports_data) == 1:
|
||||||
|
# If only one report, no need to merge files. Returns as is.
|
||||||
|
return reports_data[0].getvalue()
|
||||||
|
else:
|
||||||
|
return self._merge_pdfs(reports_data)
|
||||||
|
for res_id in res_ids:
|
||||||
|
record = self.env[self_sudo.model].browse(res_id)
|
||||||
|
attachment_name = safe_eval(self_sudo.attachment, {'object': record, 'time': time})
|
||||||
|
# Unable to compute a name for the attachment.
|
||||||
|
if not attachment_name:
|
||||||
|
continue
|
||||||
|
attachment_vals_list.append({
|
||||||
|
'name': attachment_name,
|
||||||
|
'raw': pdf_content, # stream_data['stream'].getvalue(),
|
||||||
|
'res_model': self_sudo.model,
|
||||||
|
'res_id': record.id,
|
||||||
|
'type': 'binary',
|
||||||
|
})
|
||||||
|
if attachment_vals_list:
|
||||||
|
attachment_names = ', '.join(x['name'] for x in attachment_vals_list)
|
||||||
|
try:
|
||||||
|
self.env['ir.attachment'].create(attachment_vals_list)
|
||||||
|
except AccessError:
|
||||||
|
_logger.info("Cannot save PDF report %r attachments for user %r", attachment_names, self.env.user.display_name)
|
||||||
|
else:
|
||||||
|
_logger.info("The PDF documents %r are now saved in the database", attachment_names)
|
||||||
|
return pdf_content
|
||||||
|
|
||||||
def _post_docx(self, save_in_attachment, docx_content=None, res_ids=None):
|
def _post_docx(self, save_in_attachment, docx_content=None, res_ids=None):
|
||||||
"""
|
"""
|
||||||
Добавляет сгенерированный файл в аттачменты
|
Adds generated file in attachments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def close_streams(streams):
|
def close_streams(streams):
|
||||||
for stream in streams:
|
for stream in streams:
|
||||||
try:
|
try:
|
||||||
@ -218,9 +259,7 @@ class IrActionsReport(models.Model):
|
|||||||
|
|
||||||
if len(save_in_attachment) == 1 and not docx_content:
|
if len(save_in_attachment) == 1 and not docx_content:
|
||||||
return list(save_in_attachment.values())[0].getvalue()
|
return list(save_in_attachment.values())[0].getvalue()
|
||||||
|
|
||||||
streams = []
|
streams = []
|
||||||
|
|
||||||
if docx_content:
|
if docx_content:
|
||||||
# Build a record_map mapping id -> record
|
# Build a record_map mapping id -> record
|
||||||
record_map = {
|
record_map = {
|
||||||
@ -229,7 +268,6 @@ class IrActionsReport(models.Model):
|
|||||||
[res_id for res_id in res_ids if res_id]
|
[res_id for res_id in res_ids if res_id]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
# If no value in attachment or no record specified, only append the whole docx.
|
# If no value in attachment or no record specified, only append the whole docx.
|
||||||
if not record_map or not self.attachment:
|
if not record_map or not self.attachment:
|
||||||
streams.append(docx_content)
|
streams.append(docx_content)
|
||||||
@ -250,11 +288,9 @@ class IrActionsReport(models.Model):
|
|||||||
streams.append(docx_content)
|
streams.append(docx_content)
|
||||||
else:
|
else:
|
||||||
streams.append(docx_content)
|
streams.append(docx_content)
|
||||||
|
|
||||||
if self.attachment_use:
|
if self.attachment_use:
|
||||||
for stream in save_in_attachment.values():
|
for stream in save_in_attachment.values():
|
||||||
streams.append(stream)
|
streams.append(stream)
|
||||||
|
|
||||||
if len(streams) == 1:
|
if len(streams) == 1:
|
||||||
result = streams[0].getvalue()
|
result = streams[0].getvalue()
|
||||||
else:
|
else:
|
||||||
@ -271,7 +307,7 @@ class IrActionsReport(models.Model):
|
|||||||
|
|
||||||
def _postprocess_docx_report(self, record, buffer):
|
def _postprocess_docx_report(self, record, buffer):
|
||||||
"""
|
"""
|
||||||
Непосредственно создает запись в ir.attachment
|
Creates the record in the "ir.attachment" model.
|
||||||
"""
|
"""
|
||||||
attachment_name = safe_eval(self.attachment, {"object": record, "time": time})
|
attachment_name = safe_eval(self.attachment, {"object": record, "time": time})
|
||||||
if not attachment_name:
|
if not attachment_name:
|
||||||
@ -311,19 +347,22 @@ class IrActionsReport(models.Model):
|
|||||||
else:
|
else:
|
||||||
return streams
|
return streams
|
||||||
|
|
||||||
def _render_docx(self, docids, data=None):
|
def _render_docx(self, docids: list, data: dict=None):
|
||||||
"""
|
"""
|
||||||
Получает данные для рендеринга и вызывает его.
|
Receive the data for rendering and calls for it.
|
||||||
|
|
||||||
|
docids: list of record's ids for which report is generated.
|
||||||
|
data: dict, conains "context", "report_type".
|
||||||
"""
|
"""
|
||||||
if not data:
|
if not data:
|
||||||
data = {}
|
data = {}
|
||||||
data.setdefault("report_type", "docx")
|
data.setdefault("report_type", "docx")
|
||||||
data = self._get_rendering_context(docids, data)
|
data = self._get_rendering_context(self, docids, data) # self contains current record of ir.actions.report model.
|
||||||
return self._render_docx_template(self.report_docx_template, values=data)
|
return self._render_docx_template(self.report_docx_template, values=data)
|
||||||
|
|
||||||
def _render_docx_template(self, template: bytes, values: dict = None):
|
def _render_docx_template(self, template: bytes, values: dict = None):
|
||||||
"""
|
"""
|
||||||
Непосредственно рендеринг docx файла
|
docx file rendering itself.
|
||||||
"""
|
"""
|
||||||
if values is None:
|
if values is None:
|
||||||
values = {}
|
values = {}
|
||||||
@ -388,7 +427,7 @@ class IrActionsReport(models.Model):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_pdf_from_office(content_stream):
|
def _get_pdf_from_office(content_stream):
|
||||||
"""
|
"""
|
||||||
Вызов конвертации docx в pdf с помощью gotenberg
|
Converting docx into pdf with Gotenberg service.
|
||||||
"""
|
"""
|
||||||
result = None
|
result = None
|
||||||
url = convert_pdf_from_office_url()
|
url = convert_pdf_from_office_url()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user