[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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -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'}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user