第十九章 报表引擎

篇别:第三篇 数据库与数据管理

本章学习目标

  • 能注册 ir.actions.report 并编写 QWeb 报表模板(含 report 标签与 t-call 布局)。
  • 能配置 report.paperformat自定义字体 的基本思路。
  • 了解 PDF 渲染引擎 与部署环境的关系(容器、字体、中文)。
  • 能在报表逻辑中贯彻 多公司与记录权限(避免跨公司泄露)。

导读:报表 = 数据 + QWeb + 输出格式

Odoo 报表通常由 ir.actions.report 指向 QWeb 模板,渲染为 PDF 或 HTML。数据来自 report.model 上的记录集;模板中可访问 docsdoccompany 等上下文(以渲染 API 为准)。


19.1 QWeb 报表

19.1.1 知识要点

  • report 标签(在 XML 中):绑定 modelreport_typename(模板名)、file(文件名,视结构而定)。
  • ir.actions.reportreport_name 指向 主模板 module.template_name
  • 打印入口:表单 打印 菜单;亦可 binding_model_id 绑定到模型。

19.1.2 案例

动作定义

<record id="action_report_book" model="ir.actions.report">
  <field name="name">图书卡片</field>
  <field name="model">library.book</field>
  <field name="report_type">qweb-pdf</field>
  <field name="report_name">my_library.report_book_document</field>
  <field name="report_file">my_library.report_book_document</field>
  <field name="print_report_name">'Book - %s' % (object.name)</field>
  <field name="binding_model_id" ref="model_library_book"/>
  <field name="binding_type">report</field>
</record>

模板片段

<template id="report_book_document">
  <t t-call="web.external_layout">
    <div class="page">
      <h2 t-esc="doc.name"/>
      <p>ISBN: <span t-esc="doc.isbn"/></p>
    </div>
  </t>
</template>

具体 t-callid 命名以模块 report/ 目录组织为准。

19.1.3 截图占位

图 19-1 打印菜单

19.1.4 本节练习

  1. 实操:PDF 中显示 company.logo 与公司地址。
  2. 简答print_report_name 与下载文件名关系?
  3. 判断:报表模板运行在 与当前用户相同权限 的 env 中。( )

参考答案提示:3. 通常继承用户权限;sudo 报表须格外谨慎。


19.2 PDF 渲染引擎

19.2.1 知识要点

历史上常用 wkhtmltopdf;新版本可能采用 headless Chromium 等(以 19.0 部署文档与源码为准)。

容器镜像需包含对应依赖与 字体,否则中文 tofu 或乱码

系统安装、字体、验收与排障第四十九章 §49.6

19.2.2 案例

服务器执行(示意):

wkhtmltopdf --version
# 或查看 Odoo 配置/日志中实际调用

19.2.3 截图占位

图 19-2 PDF 引擎版本或日志

19.2.4 本节练习

  1. 简答:PDF 中文乱码常见 3 种原因?
  2. 实操:在测试 PDF 中嵌入 Noto Sans SC(若印厂要求)。

参考答案提示:1. 缺字体、未嵌入子集、引擎不支持该字体格式。


19.3 自定义报表

19.3.1 知识要点

  • 循环t-foreach="docs" 或单 doc
  • 子报表t-call 借阅明细表。
  • 性能:避免在模板中 逐条 search;在 _get_report_values(若使用)中批量预取。

19.3.2 案例

class ReportBook(models.AbstractModel):
    _name = 'report.my_library.report_book_document'
    _description = 'Book Report'

    @api.model
    def _get_report_values(self, docids, data=None):
        docs = self.env['library.book'].browse(docids)
        return {
            'doc_ids': docids,
            'doc_model': 'library.book',
            'docs': docs,
            'lines': self.env['library.loan.line'].search([('book_id', 'in', docs.ids)]),
        }

类名与继承 AbstractModel 以官方报表示例为准。

19.3.3 截图占位

图 19-3 分页与表头

19.3.4 本节练习

  1. 实操:页脚 页码web.basic_layout 或自定义)。
  2. 简答:大 PDF 超时 时后端可采取什么策略?

参考答案提示:异步作业、分批渲染、简化模板。


19.4 纸张格式与自定义字体(新增)

19.4.1 知识要点

report.paperformatDPI页边距页眉页脚每页行数(部分报表类型)。

字体:在 SCSS / @font-face 或报表引擎配置中嵌入;注意 许可证

19.4.2 案例

<record id="paperformat_book_label" model="report.paperformat">
  <field name="name">图书标签</field>
  <field name="format">custom</field>
  <field name="page_height">30</field>
  <field name="page_width">50</field>
  <field name="orientation">Portrait</field>
  <field name="margin_top">2</field>
  <field name="margin_bottom">2</field>
  <field name="margin_left">2</field>
  <field name="margin_right">2</field>
</record>

19.4.3 截图占位

图 19-4 paperformat 表单

19.4.4 本节练习

  1. 实操:窄标签纸测试打印对齐。
  2. 简答DPI条码可读性 关系?

19.5 报表即网页(新增)

19.5.1 知识要点

report_type='qweb-html' 可在浏览器预览;样式与 PDF 可能不一致(打印媒体、分页)。

认证:链接需 登录态在匿名路由暴露敏感报表。

19.5.3 截图占位

图 19-5 HTML 预览

19.5.4 本节练习

  1. 简答:Web 预览与 PDF 样式不一致时 排查顺序
  2. 安全:报表 URL 带 docids 时如何防 水平越权

参考答案提示:2. 渲染前 browse + check_access 或规则过滤。


19.6 多公司与数据安全

19.6.1 知识要点

  • company 上下文:模板中 company 通常取 当前公司;跨公司汇总须 显式遍历 allowed companies分别权限检查
  • 禁止sudo() 打报表给普通用户看 全库

19.6.2 本节练习

  1. 实操:用 B 公司用户打印 A 公司记录,断言 AccessError 或空数据。

本章综合练习

  1. 实现借阅汇总表,参数 日期起止(wizard 或 data dict)。
  2. 安全:写出防止 跨公司泄露 的检查步骤(代码级提纲)。
  3. 运维:PDF 内存暴涨 时如何定位(日志、分页、模板循环)?
  4. 阅读:官方 Reporting 文档中 Paper formatreport layout 关系一句。

本章对应白皮书目录:第十九章 报表引擎。