[FIX] account,portal: fix 'download' button in account portal

The 'download' button should download *all* the attachments linked to
the move. Depending on the localization, an xml might be needed in
addition to the PDF (e.g. the CFDI and the signed PDF in Mexico).

To do that, we open a headless Send & Print wizard with only the
`download` checkbox set to True.

Secondly, do not show the 'Download' button in the portal view if no
official attachment is linked to the move (the print button still
appears).

task-3542881

Part-of: odoo/odoo#138206
This commit is contained in:
Julien Van Roy
2023-10-20 07:22:48 +00:00
parent e433414cdd
commit acca2f71ad
7 changed files with 58 additions and 31 deletions
+11 -16
View File
@@ -1,12 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import io
import zipfile
from odoo import http, _
from odoo.http import request, content_disposition
def _get_zip_headers(content, filename):
return [
('Content-Type', 'zip'),
('X-Content-Type-Options', 'nosniff'),
('Content-Length', len(content)),
('Content-Disposition', content_disposition(filename)),
]
class AccountDocumentDownloadController(http.Controller):
@http.route('/account/export_zip_documents', type='http', auth='user')
def export_zip_documents(self, **args):
@@ -16,18 +23,6 @@ class AccountDocumentDownloadController(http.Controller):
attachments = request.env['ir.attachment'].browse(ids)
attachments.check_access_rights('read')
attachments.check_access_rule('read')
# Create zip file
buffer = io.BytesIO()
with zipfile.ZipFile(buffer, 'w', compression=zipfile.ZIP_DEFLATED) as zipfile_obj:
for attachment in attachments:
zipfile_obj.writestr(attachment.display_name, attachment.raw)
content = buffer.getvalue()
headers = [
('Content-Type', 'zip'),
('X-Content-Type-Options', 'nosniff'),
('Content-Length', len(content)),
('Content-Disposition', content_disposition(filename)),
]
content = attachments._build_zip_from_attachments()
headers = _get_zip_headers(content, filename)
return request.make_response(content, headers)
+17 -2
View File
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import OrderedDict
from odoo import http, _
from odoo.osv import expression
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
from odoo.addons.account.controllers.download_docs import _get_zip_headers
from odoo.exceptions import AccessError, MissingError
from collections import OrderedDict
from odoo.http import request
@@ -121,7 +123,20 @@ class PortalAccount(CustomerPortal):
except (AccessError, MissingError):
return request.redirect('/my')
if report_type in ('html', 'pdf', 'text'):
if report_type == 'pdf' and download:
# Send & Print wizard with only the 'download' checkbox to get the official attachment(s)
template = request.env.ref(invoice_sudo._get_mail_template())
attachment_ids = invoice_sudo._generate_pdf_and_send_invoice(template, checkbox_send_mail=False, checkbox_download=True)
attachments = request.env['ir.attachment'].browse(attachment_ids)
if len(attachments) > 1:
filename = invoice_sudo._get_invoice_report_filename(extension='zip')
zip_content = attachments.sudo()._build_zip_from_attachments()
headers = _get_zip_headers(zip_content, filename)
return request.make_response(zip_content, headers)
headers = self._get_http_headers(invoice_sudo, report_type, attachments.raw, download)
return request.make_response(attachments.raw, list(headers.items()))
elif report_type in ('html', 'pdf', 'text'):
return self._show_report(model=invoice_sudo, report_type=report_type, report_ref='account.account_invoices', download=download)
values = self._invoice_get_page_view_values(invoice_sudo, access_token, **kw)
+5 -4
View File
@@ -4294,15 +4294,16 @@ class AccountMove(models.Model):
return rslt
def _get_pdf_and_send_invoice_vals(self, template):
def _get_pdf_and_send_invoice_vals(self, template, **kwargs):
return {
'mail_template_id': template.id,
'move_ids': self.ids,
'checkbox_send_mail': True,
'checkbox_download': False,
**kwargs,
}
def _generate_pdf_and_send_invoice(self, template, from_cron=True, allow_fallback_pdf=True):
def _generate_pdf_and_send_invoice(self, template, from_cron=True, allow_fallback_pdf=True, **kwargs):
""" Generate the pdf for the current invoices and send them by mail using the send & print wizard.
:param from_cron: Flag indicating if the method is called from a cron. In that case, we avoid raising any
@@ -4310,11 +4311,11 @@ class AccountMove(models.Model):
:param allow_fallback_pdf: In case of error when generating the documents for invoices, generate a
proforma PDF report instead.
"""
composer_vals = self._get_pdf_and_send_invoice_vals(template)
composer_vals = self._get_pdf_and_send_invoice_vals(template, **kwargs)
composer = self.env['account.move.send'].create(composer_vals)
# from_cron=True to log errors in chatter instead of raise
composer.action_send_and_print(from_cron=from_cron, allow_fallback_pdf=allow_fallback_pdf)
return composer.action_send_and_print(from_cron=from_cron, allow_fallback_pdf=allow_fallback_pdf)
def get_invoice_pdf_report_attachment(self):
if len(self) < 2 and self.invoice_pdf_report_id:
+9
View File
@@ -10,6 +10,7 @@ except ImportError:
from PyPDF2.utils import PdfReadError
import io
import logging
import zipfile
_logger = logging.getLogger(__name__)
@@ -17,6 +18,14 @@ _logger = logging.getLogger(__name__)
class IrAttachment(models.Model):
_inherit = 'ir.attachment'
def _build_zip_from_attachments(self):
""" Return the zip bytes content resulting from compressing the attachments in `self`"""
buffer = io.BytesIO()
with zipfile.ZipFile(buffer, 'w', compression=zipfile.ZIP_DEFLATED) as zipfile_obj:
for attachment in self:
zipfile_obj.writestr(attachment.display_name, attachment.raw)
return buffer.getvalue()
# -------------------------------------------------------------------------
# EDI
# -------------------------------------------------------------------------
+4 -1
View File
@@ -646,7 +646,10 @@ class AccountMoveSend(models.Model):
# _get_invoice_extra_attachments retrieves invoice PDF and other possible xml, etc.
attachment_ids += self._get_invoice_extra_attachments(move).ids or moves_data.get(move).get('proforma_pdf_attachment').ids
if attachment_ids:
return self._download(attachment_ids)
if from_cron:
return attachment_ids
else:
return self._download(attachment_ids)
return {'type': 'ir.actions.act_window_close'}
+10 -6
View File
@@ -489,14 +489,18 @@ class CustomerPortal(Controller):
method_name = '_render_qweb_%s' % (report_type)
report = getattr(ReportAction, method_name)(report_ref, list(model.ids), data={'report_type': report_type})[0]
reporthttpheaders = [
('Content-Type', 'application/pdf' if report_type == 'pdf' else 'text/html'),
('Content-Length', len(report)),
]
headers = self._get_http_headers(model, report_type, report, download)
return request.make_response(report, headers=list(headers.items()))
def _get_http_headers(self, model, report_type, report, download):
headers = {
'Content-Type': 'application/pdf' if report_type == 'pdf' else 'text/html',
'Content-Length': len(report),
}
if report_type == 'pdf' and download:
filename = "%s.pdf" % (re.sub('\W+', '-', model._get_report_base_filename()))
reporthttpheaders.append(('Content-Disposition', content_disposition(filename)))
return request.make_response(report, headers=reporthttpheaders)
headers['Content-Disposition'] = content_disposition(filename)
return headers
def get_error(e, path=''):
""" Recursively dereferences `path` (a period-separated sequence of dict
@@ -7,8 +7,8 @@ from odoo import models
class AccountMove(models.Model):
_inherit = "account.move"
def _get_pdf_and_send_invoice_vals(self, template):
def _get_pdf_and_send_invoice_vals(self, template, **kwargs):
# EXTENDS account
vals = super()._get_pdf_and_send_invoice_vals(template)
vals = super()._get_pdf_and_send_invoice_vals(template, **kwargs)
vals['checkbox_send_by_post'] = False
return vals