diff options
| author | Raf Geens <raf@odoo.com> | 2021-04-01 15:39:20 +0000 |
|---|---|---|
| committer | Nicolas Lempereur <nle@odoo.com> | 2021-04-09 08:00:51 +0000 |
| commit | 266a8e2a8abf936d5fbd45752ddf492d92e6a397 (patch) | |
| tree | abc4aae335fd72e87f16b1e65ad80d621e762e76 /odoo/addons/base | |
| parent | d050cf6d5891c06106aa68e2945e818cfd0b8455 (diff) | |
[FIX] base: Report which PDFs can't be read
PyPDF2 can't read some PDFs due to bugs, lack of fault-tolerance and not
supporting some encryption schemes. When invoices are uploaded directly
through the Invoices / Accounting app, that will fail for those files
because it will try to read part of them. When invoices are uploaded
through the Documents app, they're not read, which means the customer
can end up in a situation where read and merge operations (like when
printing a selection of original vendor bills) will fail.
The structural solution for this is to replace PyPDF2 with a more capable
library, like pikepdf: https://github.com/odoo/odoo/pull/64356 .
This change is too impactful for stable, and there are compatibility
concerns as well.
A possible workaround is merging the PDFs we can and bundling the rest
together in a zip file. This is possible on the level of just
_merge_pdfs, but the PDF extension and mimetype are pretty hard-coded
and the changes required to switch them to ZIP based on what happens in
_merge_pdfs seem too impactful.
Instead, this fix detects as early as possible if the PDFs can't be
read and reports it to the user. The excluded PDFs can still be
downloaded individually. In this way the user has a workaround
and does not need to resort to trial and error to figure out which PDFs
are causing the problem.
Related tickets: 2359929, 2389679
closes odoo/odoo#68964
X-original-commit: 7a3fb09dbc68797522a3191a3358db296f279b36
Signed-off-by: Nicolas Lempereur (nle) <nle@odoo.com>
Diffstat (limited to 'odoo/addons/base')
| -rw-r--r-- | odoo/addons/base/i18n/base.pot | 10 | ||||
| -rw-r--r-- | odoo/addons/base/models/ir_actions_report.py | 32 |
2 files changed, 41 insertions, 1 deletions
diff --git a/odoo/addons/base/i18n/base.pot b/odoo/addons/base/i18n/base.pot index 6dd0694ecdf..adaf921547d 100644 --- a/odoo/addons/base/i18n/base.pot +++ b/odoo/addons/base/i18n/base.pot @@ -18821,6 +18821,16 @@ msgid "" msgstr "" #. module: base +#: code:addons/base/models/ir_actions_report.py:0 +#, python-format +msgid "" +"Odoo is unable to merge the PDFs attached to the following records:\n" +"%s\n" +"\n" +"Please exclude them from the selection to continue. It's possible to still retrieve those PDFs by selecting each of the affected records individually, which will avoid merging." +msgstr "" + +#. module: base #: model:ir.module.module,shortdesc:base.module_odoo_referral msgid "Odoo referral program" msgstr "" diff --git a/odoo/addons/base/models/ir_actions_report.py b/odoo/addons/base/models/ir_actions_report.py index 643f3f21bcf..b3d95eb5c32 100644 --- a/odoo/addons/base/models/ir_actions_report.py +++ b/odoo/addons/base/models/ir_actions_report.py @@ -690,6 +690,30 @@ class IrActionsReport(models.Model): close_streams(streams) return result + def _get_unreadable_pdfs(self, streams): + unreadable_streams = [] + + writer = PdfFileWriter() + for stream in streams: + try: + reader = PdfFileReader(stream) + writer.appendPagesFromReader(reader) + except utils.PdfReadError: + unreadable_streams.append(stream) + + return unreadable_streams + + def _raise_on_unreadable_pdfs(self, streams, stream_record): + unreadable_pdfs = self._get_unreadable_pdfs(streams) + if unreadable_pdfs: + records = [stream_record[s].name for s in unreadable_pdfs if s in stream_record] + raise UserError(_( + "Odoo is unable to merge the PDFs attached to the following records:\n" + "%s\n\n" + "Please exclude them from the selection to continue. It's possible to " + "still retrieve those PDFs by selecting each of the affected records " + "individually, which will avoid merging.") % "\n".join(records)) + def _merge_pdfs(self, streams): writer = PdfFileWriter() for stream in streams: @@ -741,6 +765,8 @@ class IrActionsReport(models.Model): return self_sudo.with_context(context)._render_qweb_html(res_ids, data=data)[0] save_in_attachment = OrderedDict() + # Maps the streams in `save_in_attachment` back to the records they came from + stream_record = dict() if res_ids: # Dispatch the records by ones having an attachment and ones requesting a call to # wkhtmltopdf. @@ -751,7 +777,9 @@ class IrActionsReport(models.Model): for record_id in record_ids: attachment = self_sudo.retrieve_attachment(record_id) if attachment: - save_in_attachment[record_id.id] = self_sudo._retrieve_stream_from_attachment(attachment) + stream = self_sudo._retrieve_stream_from_attachment(attachment) + save_in_attachment[record_id.id] = stream + stream_record[stream] = record_id if not self_sudo.attachment_use or not attachment: wk_record_ids += record_id else: @@ -763,6 +791,7 @@ class IrActionsReport(models.Model): # - The report is not fully present in attachments. if save_in_attachment and not res_ids: _logger.info('The PDF report has been generated from attachments.') + self._raise_on_unreadable_pdfs(save_in_attachment.values(), stream_record) return self_sudo._post_pdf(save_in_attachment), 'pdf' if self.get_wkhtmltopdf_state() == 'install': @@ -792,6 +821,7 @@ class IrActionsReport(models.Model): set_viewport_size=context.get('set_viewport_size'), ) if res_ids: + self._raise_on_unreadable_pdfs(save_in_attachment.values(), stream_record) _logger.info('The PDF report has been generated for model: %s, records %s.' % (self_sudo.model, str(res_ids))) return self_sudo._post_pdf(save_in_attachment, pdf_content=pdf_content, res_ids=html_ids), 'pdf' return pdf_content, 'pdf' |
