第十九章 报表引擎
篇别:第三篇 数据库与数据管理
本章学习目标
- 能注册
ir.actions.report并编写 QWeb 报表模板(含report标签与t-call布局)。 - 能配置
report.paperformat与 自定义字体 的基本思路。 - 了解 PDF 渲染引擎 与部署环境的关系(容器、字体、中文)。
- 能在报表逻辑中贯彻 多公司与记录权限(避免跨公司泄露)。
导读:报表 = 数据 + QWeb + 输出格式
Odoo 报表通常由 ir.actions.report 指向 QWeb 模板,渲染为 PDF 或 HTML。数据来自 report.model 上的记录集;模板中可访问 docs、doc、company 等上下文(以渲染 API 为准)。
19.1 QWeb 报表
19.1.1 知识要点
report标签(在 XML 中):绑定model、report_type、name(模板名)、file(文件名,视结构而定)。ir.actions.report:report_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-call 与 id 命名以模块 report/ 目录组织为准。
19.1.3 截图占位

19.1.4 本节练习
- 实操:PDF 中显示
company.logo与公司地址。 - 简答:
print_report_name与下载文件名关系? - 判断:报表模板运行在 与当前用户相同权限 的 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.4 本节练习
- 简答:PDF 中文乱码常见 3 种原因?
- 实操:在测试 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.4 本节练习
- 实操:页脚 页码(
web.basic_layout或自定义)。 - 简答:大 PDF 超时 时后端可采取什么策略?
参考答案提示:异步作业、分批渲染、简化模板。
19.4 纸张格式与自定义字体(新增)
19.4.1 知识要点
report.paperformat:DPI、页边距、页眉页脚、每页行数(部分报表类型)。
字体:在 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.4 本节练习
- 实操:窄标签纸测试打印对齐。
- 简答:
DPI与 条码可读性 关系?
19.5 报表即网页(新增)
19.5.1 知识要点
report_type='qweb-html' 可在浏览器预览;样式与 PDF 可能不一致(打印媒体、分页)。
认证:链接需 登录态;勿在匿名路由暴露敏感报表。
19.5.3 截图占位

19.5.4 本节练习
- 简答:Web 预览与 PDF 样式不一致时 排查顺序?
- 安全:报表 URL 带 docids 时如何防 水平越权?
参考答案提示:2. 渲染前 browse + check_access 或规则过滤。
19.6 多公司与数据安全
19.6.1 知识要点
company上下文:模板中company通常取 当前公司;跨公司汇总须 显式遍历 allowed companies 并 分别权限检查。- 禁止:
sudo()打报表给普通用户看 全库。
19.6.2 本节练习
- 实操:用 B 公司用户打印 A 公司记录,断言 AccessError 或空数据。
本章综合练习
- 实现:借阅汇总表,参数 日期起止(wizard 或
datadict)。 - 安全:写出防止 跨公司泄露 的检查步骤(代码级提纲)。
- 运维:PDF 内存暴涨 时如何定位(日志、分页、模板循环)?
- 阅读:官方 Reporting 文档中 Paper format 与 report layout 关系一句。
本章对应白皮书目录:第十九章 报表引擎。