Odoo14 CE:采购订单附件PDF加电子签名及嵌入空白问题解决
问题解决与实现思路
一、当前嵌入PDF空白的修复方案
你用iframe嵌入base64 PDF在QWeb报表里显示空白,核心原因是wkhtmltopdf(Odoo默认PDF渲染引擎)不支持通过data URI在iframe中嵌入PDF。以下是两种针对性解决方案:
1. 界面内预览方案(无需导出报表)
如果仅需在Odoo界面查看供应商报价单,可修改QWeb模板生成附件直接链接,避免iframe的兼容性问题:
<?xml version="1.0"?> <t t-name="purchase.report_purchasequotation_document"> <t t-call="web.basic_layout"> <t t-foreach="docs" t-as="doc"> <div class="page"> <t t-if="doc.Proformat_fournisseur"> <a t-att-href="'/web/content/' + doc.id + '/Proformat_fournisseur?model=purchase.order&download=true'" target="_blank">点击查看供应商报价单PDF</a> </t> </div> </t> </t> </t>
用户点击链接即可打开原始PDF,不会出现空白。
2. 导出合并PDF方案(核心需求适配)
要实现「供应商报价单+电子签名」合并导出,必须通过Python代码处理PDF拼接,步骤如下:
步骤1:安装PDF处理依赖
在Odoo服务器执行:
pip install PyPDF2
步骤2:重写报表逻辑实现PDF合并
继承采购订单报表模型,添加PDF合并逻辑:
from odoo import models, api from PyPDF2 import PdfMerger, PdfReader import io import base64 class PurchaseReportInherit(models.Model): _inherit = 'purchase.report_purchasequotation' @api.model def _get_report_values(self, docids, data=None): report_values = super()._get_report_values(docids, data=data) docs = report_values.get('docs') # 替换为你的电子签名附件ID SIGN_ATTACHMENT_ID = 123 for doc in docs: if doc.Proformat_fournisseur: # 读取上传的供应商报价单PDF proformat_pdf = io.BytesIO(base64.b64decode(doc.Proformat_fournisseur)) proformat_reader = PdfReader(proformat_pdf) # 读取系统存储的电子签名PDF sign_attachment = self.env['ir.attachment'].browse(SIGN_ATTACHMENT_ID) sign_pdf = io.BytesIO(base64.b64decode(sign_attachment.datas)) sign_reader = PdfReader(sign_pdf) # 合并PDF:报价单在前,签名页在后 merger = PdfMerger() merger.append(proformat_reader) merger.append(sign_reader) # 生成合并后的base64格式PDF merged_pdf_io = io.BytesIO() merger.write(merged_pdf_io) merged_pdf_io.seek(0) doc.merged_proformat = base64.b64encode(merged_pdf_io.getvalue()).decode('utf-8') report_values['docs'] = docs return report_values
步骤3:自定义打印按钮直接下载合并PDF
如果不需要修改原有报表,可给采购订单添加自定义按钮,直接触发合并下载:
class PurchaseOrderInherit(models.Model): _inherit = 'purchase.order' def action_print_proformat_with_sign(self): self.ensure_one() if not self.Proformat_fournisseur: return SIGN_ATTACHMENT_ID = 123 # 重复上述PDF合并逻辑 proformat_pdf = io.BytesIO(base64.b64decode(self.Proformat_fournisseur)) proformat_reader = PdfReader(proformat_pdf) sign_attachment = self.env['ir.attachment'].browse(SIGN_ATTACHMENT_ID) sign_pdf = io.BytesIO(base64.b64decode(sign_attachment.datas)) sign_reader = PdfReader(sign_pdf) merger = PdfMerger() merger.append(proformat_reader) merger.append(sign_reader) merged_pdf_io = io.BytesIO() merger.write(merged_pdf_io) merged_pdf_io.seek(0) return { 'type': 'ir.actions.act_url', 'url': 'data:application/pdf;base64,' + base64.b64encode(merged_pdf_io.getvalue()).decode('utf-8'), 'target': 'new', }
二、其他优化实现思路
1. 直接在报价单底部插入签名图片
如果不想新增签名页,可将签名图片直接插入报价单最后一页底部,用ReportLab实现:
from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter from PyPDF2 import PdfReader, PdfWriter import io def insert_sign_to_last_page(proformat_pdf_bytes, sign_image_path): # 创建带签名的临时画布 packet = io.BytesIO() can = canvas.Canvas(packet, pagesize=letter) # 调整坐标适配你的PDF页面(示例为底部居中) can.drawImage(sign_image_path, 200, 50, width=150, height=50) can.save() packet.seek(0) sign_pdf = PdfReader(packet) proformat_pdf = PdfReader(io.BytesIO(proformat_pdf_bytes)) output = PdfWriter() # 复制所有页,最后一页合并签名 for idx in range(len(proformat_pdf.pages)): page = proformat_pdf.pages[idx] if idx == len(proformat_pdf.pages)-1: page.merge_page(sign_pdf.pages[0]) output.add_page(page) output_stream = io.BytesIO() output.write(output_stream) output_stream.seek(0) return output_stream.getvalue()
2. 动态渲染签名区域再合并
利用Odoo QWeb报表动态生成签名区域(包含用户名、日期等变量),再渲染成PDF合并:
# 渲染自定义签名报表为PDF sign_report = self.env.ref('your_module.report_dynamic_signature') sign_pdf, _ = sign_report.render_qweb_pdf([self.id]) # 后续将sign_pdf与供应商报价单PDF合并即可
内容的提问来源于stack exchange,提问作者Wassim Benkoulal
相关产品推荐
相关产品推荐

