构建 PDF 报表¶
Important
本教程是 服务器框架 101 教程的扩展。请确保您已完成该教程,并使用您构建的 estate 模块作为本教程练习的基础。
我们之前已经在 QWeb 简介 中接触过 QWeb,当时用它构建了看板视图。现在我们将深入探讨 QWeb 的另一个主要用途:创建 PDF 报表。一个常见的业务需求是能够创建发送给客户和内部使用的文档。这些报表可以在有条理的模板中汇总和显示信息,以不同方式支持业务。此外,Odoo 只需极少的额外工作就能为报表添加公司的页眉和页脚。
与本主题相关的文档可参阅 QWeb 模板、QWeb 报表,以及动作参考中的 报表动作(ir.actions.report) 部分。
文件结构¶
PDF 报表的主体是它的 QWeb 模板。通常还需要一个相应的 ir.actions.report,以便将报表纳入模块的业务逻辑。文件名及其存放位置没有严格规定,但这两部分通常分别存储在模块目录顶层 report 文件夹中的 2 个独立文件里。如果模块有许多或多个较长的报表模板,通常会按逻辑将它们组织到以所含报表命名的不同文件中。报表的所有动作通常存储在同一个以 _reports.xml 结尾的文件中,无论其中包含多少个报表。
因此,您的工作目录树预期大致如下:
estate
├── models
│ ├── *.py
│ └── __init__.py
├── report
│ ├── estate_property_templates.xml
│ └── estate_property_reports.xml
├── security
│ └── ir.model.access.csv
├── views
│ └── *.xml
├── __init__.py
└── __manifest__.py
别忘了将模板和动作视图所在的文件添加到 __manifest__.py 中。在本例中,您需要把这些文件加入 data 列表,并且请记住清单中列出的文件是按顺序加载的!
基础报表¶
Note
目标:在本节结束时,我们将能够打印一份显示某处房产所有报价的报表。
在房地产示例中,有许多有用的报表可以创建。我们可以创建的一个简单报表是显示某处房产所有报价的报表。
报表数据¶
在开始之前,我们首先需要一些数据来填充报表,否则本教程会显得索然无味。创建报表时,您需要一些数据来测试报表代码,并检查最终外观是否符合预期。最好使用能覆盖大部分或全部预期用例的数据来测试。对于我们这个简单报表,一组有代表性的数据是:
至少 3 处房产:1 处为 “sold”(已售),1 处为 “offer received”(已收到报价),1 处为 “new”(新建)。
为 “sold” 和 “offer received” 状态的房产准备至少 2-3 个报价
如果您还没有这样的一组数据,可以选择以下方式之一:
完成 定义模块数据 教程(如果尚未完成),并将这些额外情形添加到您的演示数据中(您可能需要创建一个新数据库来加载演示数据)。
在数据库中手动创建这些数据。
将这个 数据文件 复制到 estate 模块的一个新目录(data)中,并将 这几行 复制到您的 __manifest__.py 文件中(您可能需要创建一个新数据库来加载演示数据)。
在继续之前,请在数据库中逐条查看您的数据,确保数据符合预期。当然,您也可以在编写报表代码之后再添加数据,但那样就无法在编写过程中逐步测试代码的各个部分。对于复杂的报表,这最终会让检查错误和调试代码变得更加困难。
最小模板¶
在 报表模板 文档的 “最小可行模板” 部分可以查看一个最小可行模板。我们可以修改这个示例,构建我们的最小房产报价模板文件:
<?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 章:准备好来点动作了吗? 中所学的那样。这对于只在特定条件下才有意义的报表非常方便。例如,如果我们想制作 “Final Sale”(最终销售)报表,就可以将它链接到一个 “Print Sale Info”(打印销售信息)按钮,该按钮仅在房产为 “Sold”(已售)时才出现在表单视图中。
您可能已经注意到,或者好奇为什么我们的报表模板要遍历一个记录集。当模板接收到多条记录时,它可以为所有记录生成一份 PDF 报表。在列表视图中选中多条记录后使用打印菜单,即可看到这一效果。
制作报表¶
最后,您现在已经知道在哪里创建文件以及文件内容应该是什么样子。祝您报表制作愉快!
Exercise
制作一份报表。
将最小模板小节中的房产报价报表添加到 Property(房产)视图的打印菜单中。
通过添加更多数据来完善报表。请参阅本节的 目标 ,看看还能添加哪些额外数据,也欢迎添加更多。
附加练习:添加一些逻辑让报表更灵活:当房产还没有任何报价时,不创建表格,而是写上一些关于暂无报价的说明。提示:您需要用到
t-if和t-else。
记得检查您的 PDF 报表是否与数据相符,符合预期。
子模板¶
Note
目标:在本节结束时,我们将拥有一个在 2 个报表中使用的子模板。
使用子模板主要有两个原因。一是当模板特别长或特别复杂时,让代码更易于阅读。二是尽可能复用代码。我们简单的房产报价报表很有用,但列出房产报价信息并不只对一个报表模板有用。例如,一个列出某销售员所有房产报价的报表。
看看您能否通过阅读相关 文档 和/或查看一个 示例 来理解如何调用子模板(请记住,无论用于 Odoo 中的报表还是视图,QWeb 都使用相同的控制流。)
Exercise
创建并使用一个子模板。
将报价的表格部分拆分到独立的模板中。之后记得检查您的原报表仍能正常打印。
为
res.users添加一个新报表,用于打印其表单视图中(即在“设置”应用中)可见的所有房地产信息,并在同一报表中包含这些房产的报价。提示:由于这里的binding_model_id不在 estate 模块中,您需要使用ref="base.model_res_users"。您的最终结果应该与本节 目标 中的图片类似。
记得检查您的报表是否与数据相符,符合预期!
报表继承¶
Note
目标:在本节结束时,我们将在 estate_account 模块中继承房产报表。
QWeb 中的继承使用与 视图继承 相同的 xpath 元素。不过 QWeb 模板引用父模板的方式有所不同,而且更简单:只需在 template 元素中添加 inherit_id 属性,并将其设置为 module.parent_template_id 即可。
我们没有在 estate 的任何模型中添加新字段,但仍可以为现有的房产报表添加信息。例如,我们知道任何“已售出”的房产都已经创建了发票,因此我们可以将这一信息添加到报表中。
Exercise
继承一个报表。
扩展房产报表,加入一些发票信息。您可以参考本节的 目标 寻找灵感(即当房产完成时打印一行,否则什么都不打印)。
同样,记得检查您的报表是否与数据相符,符合预期!
其他特性¶
以下所有附加特性都在 QWeb 报表 文档中有进一步介绍,包括如何实现它们。
翻译¶
我们都知道,得益于自动和手动翻译,Odoo 支持多种语言。QWeb 报表也不例外!请注意,如果模板的文本内容中有多余的空格,翻译有时会无法正常工作,因此请尽量避免空格(尤其是前导空格)。
报表即网页¶
您可能已经听腻了“QWeb 生成 HTML”这句话,但我们还要再说一遍!用 QWeb 编写报表的一个巧妙特性是可以在网页浏览器中查看报表。如果您想嵌入一个指向特定报表的超链接,这会很有用。请注意,常规的安全检查仍然适用,以防止未经授权的用户访问报表。
条形码¶
Odoo 内置了条形码图片生成器,允许在报表中嵌入条形码。查看相应的 代码 以了解所有受支持的条形码类型。