构建 PDF 报表¶
重要
本教程是 服务器框架入门 教程的延伸。请确保你已完成该教程,并以你构建的 estate 模块作为本教程练习的基础。
我们之前 了解了 QWeb,当时用它来构建看板视图。现在我们将展开介绍 QWeb 的另一项主要用途:创建 PDF 报表。常见的业务需求是能够创建文档以发送给客户并在内部使用。这些报表可用于在结构化的模板中汇总和展示信息,以多种方式支持业务。Odoo 还可以用最小的额外投入为报表添加公司页眉和页脚。
与本主题相关的文档可在 QWeb 模板、QWeb 报表 以及 Actions 参考的 报表动作(ir.actions.report) 小节中找到。
文件结构¶
PDF 报表的主体是其 QWeb 模板。它通常还需要一个对应的 ir.actions.report 来将该报表纳入模块的业务逻辑中。文件名或其位置没有严格的规则,但这两部分通常存储在模块目录顶层 report 文件夹内的两个独立文件中。如果模块有许多或不止一个较长的报表模板,它们通常按逻辑分散在以所含报表命名的不同文件中。报表的所有动作通常存储在同一个以 _reports.xml 结尾的文件中,无论其中包含多少个报表。
因此,你的工作目录树预期看起来像这样:
estate
├── models
│ ├── *.py
│ └── __init__.py
├── report
│ ├── estate_property_templates.xml
│ └── estate_property_reports.xml
├── security
│ └── ir.access.csv
├── views
│ └── *.xml
├── __init__.py
└── __manifest__.py
别忘了将模板和动作视图所在的文件添加到 __manifest__.py 中。在本例中,你需要将这些文件添加到 data 列表中,并记住清单文件中列出的文件是按顺序加载的!
基本报表¶
注解
目标:在本节结束时,我们将能够打印一个显示某房产所有报价的报表。
在我们的房地产示例中,有许多有用的报表可以创建。我们可以创建的一个简单报表就是显示某房产所有报价的报表。
报表数据¶
在做任何事之前,我们首先需要一些数据来填充报表,否则本教程就不会很有趣。创建报表时,你需要一些数据来测试报表代码并检查最终外观是否符合预期。最好使用能覆盖大部分或全部预期用例的数据进行测试。适合我们简单报表的一组良好的代表性数据是:
至少 3 个房产,其中 1 个为 “sold”,1 个为 “offer received”,1 个为 “new”。
至少为我们的 “sold” 和 “offer received” 房产提供 2-3 个报价
如果你还没有这样一组数据,你可以选择:
完成 定义模块数据 教程(如果你还没完成),并将额外的情况添加到演示数据中(你可能需要创建一个新数据库来加载演示数据)。
在数据库中手动创建数据。
将此 数据文件 复制到 estate 模块中的一个新目录(data)中,并将 这些行 复制到你的 __manifest__.py 文件中(你可能需要创建一个新数据库来加载演示数据)。
在继续之前,请在你的数据库中逐一查看数据,确保数据符合预期。当然,你可以在编写报表代码之后再添加数据,但那样你就无法在编写过程中逐步测试代码的各个部分。对于复杂的报表,从长远来看,这会让检查错误和调试代码变得更加困难。
最小模板¶
一个最小可行模板可在 报表模板 文档的 “Minimal viable template” 小节中查看。我们可以修改此示例来构建最小的房产报价模板文件:
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="report_property_offers">
<t t-foreach="docs" t-as="property">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<div class="page">
<h2>
<span t-field="property.name"/>
</h2>
<div>
<strong>Expected Price: </strong>
<span t-field="property.expected_price"/>
</div>
<table class="table">
<thead>
<tr>
<th>Price</th>
</tr>
</thead>
<tbody>
<t t-set="offers" t-value="property.mapped('offer_ids')"/>
<tr t-foreach="offers" t-as="offer">
<td>
<span t-field="offer.price"/>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</t>
</template>
</odoo>
我们文件中大多数 Odoo 特有的(即非 HTML 的)项目都在最小可行模板小节中作了说明。我们模板中的一些额外特性是:
使用
class="table"属性,让我们的表格具有不错的格式。你的报表模板中可以使用 Twitter Bootstrap(本例中我们使用其 table 类)和 Font Awesome(可用于添加图标)的类。使用
t-set、t-value、t-foreach和t-as,以便遍历所有offer_ids。
如果你已经熟悉网站模板引擎,那么 QWeb 指令(即 t- 命令)可能无需过多解释,你可以直接查看其 文档 并跳到下一小节。
否则建议你多了解一些它们(Wikipedia 有很好的高层次描述),但总体思路是:QWeb 提供了根据 Odoo 数据和简单命令动态生成 Web 代码的能力。也就是说,QWeb 可以访问记录集数据(及方法),并处理设置和访问临时变量等简单的编程操作。例如,在上面的示例中:
t-set创建一个名为 “offers” 的临时变量,其值由t-value设为当前estate.property记录集的offer_ids。t-foreach和t-as的用法等同于以下 Python:
for offer in offers:
报表动作¶
现在我们有了模板,还需要通过 ir.actions.report 让应用能够访问它。ir.actions.report 的实际示例见 此处,对应 这个模板。其内容在 文档 中均有说明。
ir.actions.report 主要通过模型视图的打印菜单使用。在实际示例中,binding_model_id 指定报表应显示在哪些模型的视图中,Odoo 会自动为你添加。报表动作的另一个常见用法是把它链接到按钮,正如我们在 第 9 章:准备好采取行动了吗? 中学到的。对于只在特定条件下才有意义的报表,这很方便。例如,如果我们要制作一份“最终销售”报表,就可以把它链接到一个“打印销售信息”按钮,该按钮只在房产状态为“已售出”时出现在表单视图中。
你可能已经注意到,或者想知道为什么我们的报表模板要遍历一个记录集。当模板接收到多条记录时,它可以为所有记录生成一份 PDF 报表。在列表视图中选中多条记录后使用打印菜单,就能演示这一点。
制作报表¶
最后,你现在知道该在哪里创建文件,以及文件的内容应该是什么样子。祝你制作报表愉快!
练习
制作一个报表。
把“最小模板”小节中的房产报价报表添加到房产视图的打印菜单中。
通过添加更多数据来改进报表。请参阅本节的 目标 以了解可以添加哪些额外数据,也欢迎继续添加更多内容。
附加练习:加入一些逻辑让报表更加灵活——当房产没有任何报价时,不创建表格,而是写一段说明“暂无报价”的文字。提示:你需要使用
t-if和t-else。
记得检查你的 PDF 报表是否与数据相符。
子模板¶
注解
目标:本节结束时,我们将拥有一个可以在 2 份报表中使用的子模板。
使用子模板主要有两个原因。一是处理超长或复杂的模板时,可以让代码更易读。二是尽可能复用代码。我们这份简单的房产报价报表很有用,但列出房产报价信息的用途并不限于一份报表模板。例如,有一份报表会列出某位销售员名下所有房产的报价。
试着通过阅读相关 文档 和/或查看一个 示例 来理解如何调用子模板(请记住,无论用于 Odoo 中的报表还是视图,QWeb 使用的控制流都是相同的)。
练习
创建并使用子模板。
把报价中的表格部分拆分成单独的模板。之后记得检查原来的报表是否仍能正确打印。
为
res.users新增一份报表,用于打印在其表单视图中可见的所有房地产房产(即“设置”应用中的内容)。在同一份报表中包含这些销售员每处房产的报价。提示:由于此处的binding_model_id不在 estate 模块内,你需要使用ref="base.model_res_users"。最终结果应与本节 目标 中的图片类似。
记得检查你的报表是否与数据相符!
报表继承¶
注解
目标:本节结束时,我们将在 estate_account 模块中继承房产报表。
QWeb 中的继承使用与 视图继承 相同的 xpath 元素。不过,QWeb 模板引用其父模板的方式有所不同。更简单的做法是,只需在 template 元素上添加 inherit_id 属性,并将其设置为 module.parent_template_id。
我们并没有在 estate_account 中为任何 estate 模型添加新字段,但仍可以往现有的房产报表中添加信息。例如,我们知道任何“已售出”的房产都已经为其创建了发票,因此可以把这一信息加入报表。
练习
继承一个报表。
扩展房产报表,加入一些关于发票的信息。你可以参考本节 目标 获取灵感(即:当房产状态为“完成”时打印一行,否则不打印任何内容)。
同样,记得检查你的报表是否与数据相符!
附加功能¶
以下所有附加功能在 QWeb 报表 文档中都有更详细的说明,包括如何实现每一项。
多语种翻译¶
我们都知道,得益于自动翻译和人工翻译,Odoo 可以在多种语言中使用。QWeb 报表也不例外!请注意,如果模板文本内容中存在多余的空格,翻译有时会无法正常工作,因此请尽量避免这些空格(尤其是行首空格)。
报表即网页¶
你可能已经听腻了“QWeb 生成 HTML”这句话,但我们还要再说一次!用 QWeb 编写报表的一个妙处是,它们可以在 Web 浏览器中查看。如果你想嵌入指向某份报表的超链接,这会很有用。请注意,常规的安全检查仍会生效,以防未授权用户访问报表。
条码作业¶
Odoo 内置了条形码图像生成器,可以把条形码嵌入报表中。查看相应的 代码 即可了解支持的所有条形码类型。