[IMP] account: add Vendor Bill ref. on Invoice PDF
The goal is to add a digital stamp on the Invoice PDF stating the name of its related Vendor Bill. Auditors may request printed copies of all (original) Invoices as well as the possibility to relate each document to a posted entry (Vendor Bill). task-2755372 closes odoo/odoo#86127 X-original-commit: f0ea39791c70cc63fe39665514e5344a56ed5b71 Signed-off-by: William André (wan) <wan@odoo.com> Signed-off-by: John Laterre <jol@odoo.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, api, _
|
||||
from odoo import models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import pdf
|
||||
|
||||
|
||||
class IrActionsReport(models.Model):
|
||||
_inherit = 'ir.actions.report'
|
||||
@@ -45,3 +47,12 @@ class IrActionsReport(models.Model):
|
||||
raise UserError(_("Only invoices could be printed."))
|
||||
|
||||
return super()._render_qweb_pdf(res_ids=res_ids, data=data)
|
||||
|
||||
def _retrieve_stream_from_attachment(self, attachment):
|
||||
# Overridden in order to add a banner in the upper right corner of the exported Vendor Bill PDF.
|
||||
stream = super()._retrieve_stream_from_attachment(attachment)
|
||||
vendor_bill_export = self.env.ref('account.action_account_original_vendor_bill')
|
||||
if self == vendor_bill_export and attachment.mimetype == 'application/pdf':
|
||||
record = self.env[attachment.res_model].browse(attachment.res_id)
|
||||
return pdf.add_banner(stream, record.name, logo=True)
|
||||
return stream
|
||||
|
||||
@@ -9,6 +9,11 @@ from logging import getLogger
|
||||
from PyPDF2 import PdfFileWriter, PdfFileReader
|
||||
from PyPDF2.generic import DictionaryObject, NameObject, ArrayObject, DecodedStreamObject, NumberObject, createStringObject, ByteStringObject
|
||||
from zlib import compress, decompress
|
||||
from PIL import Image
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.units import cm
|
||||
from reportlab.lib.utils import ImageReader
|
||||
from reportlab.pdfgen import canvas
|
||||
|
||||
try:
|
||||
from fontTools.ttLib import TTFont
|
||||
@@ -78,6 +83,67 @@ def rotate_pdf(pdf):
|
||||
writer.write(_buffer)
|
||||
return _buffer.getvalue()
|
||||
|
||||
|
||||
def add_banner(pdf_stream, text=None, logo=False, thickness=2 * cm):
|
||||
""" Add a banner on a PDF in the upper right corner, with Odoo's logo (optionally).
|
||||
|
||||
:param pdf_stream (BytesIO): The PDF stream where the banner will be applied.
|
||||
:param text (str): The text to be displayed.
|
||||
:param logo (bool): Whether to display Odoo's logo in the banner.
|
||||
:param thickness (float): The thickness of the banner in pixels.
|
||||
:return (BytesIO): The modified PDF stream.
|
||||
"""
|
||||
|
||||
old_pdf = PdfFileReader(pdf_stream, strict=False, overwriteWarnings=False)
|
||||
old_pdf.getNumPages()
|
||||
packet = io.BytesIO()
|
||||
can = canvas.Canvas(packet)
|
||||
odoo_logo = Image.open(file_open('base/static/img/main_partner-image.png', mode='rb'))
|
||||
odoo_color = colors.Color(113 / 255, 75 / 255, 103 / 255, 0.8)
|
||||
|
||||
for p in range(old_pdf.getNumPages()):
|
||||
page = old_pdf.getPage(p)
|
||||
width = float(abs(page.mediaBox.getWidth()))
|
||||
height = float(abs(page.mediaBox.getHeight()))
|
||||
|
||||
can.translate(width, height)
|
||||
can.rotate(-45)
|
||||
|
||||
# Draw banner
|
||||
path = can.beginPath()
|
||||
path.moveTo(-width, -thickness)
|
||||
path.lineTo(-width, -2 * thickness)
|
||||
path.lineTo(width, -2 * thickness)
|
||||
path.lineTo(width, -thickness)
|
||||
can.setFillColor(odoo_color)
|
||||
can.drawPath(path, fill=1, stroke=False)
|
||||
|
||||
# Insert text (and logo) inside the banner
|
||||
can.setFontSize(10)
|
||||
can.setFillColor(colors.white)
|
||||
can.drawRightString(0.75 * thickness, -1.45 * thickness, text)
|
||||
logo and can.drawImage(
|
||||
ImageReader(odoo_logo), 0.25 * thickness, -2.05 * thickness, 40, 40, mask='auto', preserveAspectRatio=True)
|
||||
|
||||
can.showPage()
|
||||
|
||||
can.save()
|
||||
|
||||
# Merge the old pages with the watermark
|
||||
watermark_pdf = PdfFileReader(packet, overwriteWarnings=False)
|
||||
new_pdf = PdfFileWriter()
|
||||
for p in range(old_pdf.getNumPages()):
|
||||
new_page = old_pdf.getPage(p)
|
||||
new_page.mergePage(watermark_pdf.getPage(p))
|
||||
new_pdf.addPage(new_page)
|
||||
|
||||
# Write the new pdf into a new output stream
|
||||
output = io.BytesIO()
|
||||
new_pdf.write(output)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
# by default PdfFileReader will overwrite warnings.showwarning which is what
|
||||
# logging.captureWarnings does, meaning it essentially reverts captureWarnings
|
||||
# every time it's called which is undesirable
|
||||
|
||||
Reference in New Issue
Block a user