You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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&amp;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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 03:40:13