Odoo 打印功能架构与工作流程深度剖析

一、Odoo 打印架构与核心概念

Odoo 的报表和打印功能是其核心业务能力之一,允许用户将系统中的数据以结构化、美观的格式输出,通常是 PDF 或 HTML。Odoo 18 沿袭并优化了其成熟的报表架构,该架构主要依赖于 ir.actions.report 动作、QWeb 模板引擎以及外部的 wkhtmltopdf 工具(用于生成 PDF)。

1. 核心组件概览

在深入工作流程之前,先认识一下构成 Odoo 打印功能的几个关键组件:

  • ir.actions.report (报表动作): 这是 Odoo 中定义一个报表行为的数据库记录。它将用户界面上的触发点(如按钮)与特定的报表逻辑、数据源模型、报表模板以及输出格式关联起来。它是整个报表生成流程的入口和协调者。
  • Python 模型/方法: 报表所需的数据通常通过 Odoo 的 Python 模型获取。ir.actions.report 指定了数据来源的模型,并且通常会调用该模型上的特定方法(如 _get_report_values)来准备传递给报表模板的数据。
  • QWeb 模板引擎: Odoo 自有的 XML 模板引擎。报表的布局和内容使用 QWeb 模板定义,这些模板本质上是带有 QWeb 指令的 HTML 结构。QWeb 负责将 Python 提供的数据与模板结合,生成最终的 HTML 输出。
  • wkhtmltopdf: 一个独立的、开源的命令行工具,用于将 HTML 网页转换为 PDF 文档。Odoo 在生成 PDF 报表时,会将 QWeb 生成的 HTML 输出传递给 wkhtmltopdf 进行转换。
  • report 模块: Odoo 的官方模块,提供了报表动作的处理逻辑、QWeb 报表渲染的基础结构以及与 wkhtmltopdf 集成的接口。
  • 客户端 (Web 浏览器): 用户通过 Odoo Web 界面触发报表动作,并接收最终生成的报表文件。

2. 报表打印的完整生命周期与工作流程

从用户点击打印按钮到最终文件生成的完整流程可以分解为以下步骤:

  1. 用户触发报表动作 (客户端):
    • 用户在 Odoo 界面上(通常是某个记录的表单视图或列表视图)点击一个配置为触发报表动作的按钮。
    • 这个按钮在 XML 视图定义中通常是
    • 客户端的 JavaScript 代码捕获这个点击事件,并向 Odoo 服务器发起一个 RPC (Remote Procedure Call) 请求,告知服务器执行指定的动作。
  2. 服务器接收并处理动作请求 (服务器端 - Odoo Framework):
    • Odoo 服务器接收到客户端的 RPC 请求。
    • Odoo 的动作处理机制识别出这是一个执行特定动作的请求。
    • 服务器根据请求中提供的动作 ID 或外部 ID,在数据库中查找对应的 ir.actions.report 记录。
  3. 加载报表动作配置 (ir.actions.report):
    • 服务器从数据库加载 ir.actions.report 记录的所有配置信息,包括:
      • report_type (例如 qweb-pdf, qweb-html)
      • model (报表数据来源的主要模型)
      • report_name / report_file (报表的内部名称,常用于文件名或查找模板)
      • template_id (关联的 QWeb 模板视图 ir.ui.view)
      • paperformat_id (关联的纸张格式 report.paperformat,仅对 PDF 有效)
      • binding_model_id (动作绑定的模型)
      • binding_type (绑定类型,如 report)
      • 其他参数 (如 groups_id 控制访问权限)。
  4. 准备报表数据 (服务器端 - Python):
    • Odoo 报表模块(通常是 odoo.addons.report.models.report 中的逻辑)根据 ir.actions.report 中指定的 model 和用户选择的记录 ID (docids),调用相应模型上的数据准备方法。
    • 标准的做法是调用模型上的 _get_report_values(self, docids, data=None) 方法。
    • 开发者在该方法中编写逻辑,根据 docids 查询数据库,获取报表所需的所有数据(主记录、关联记录、计算字段等)。
    • 该方法返回一个字典,这个字典中的键值对将作为上下文变量传递给 QWeb 模板。通常,这个字典包含一个键 docs,其值是用户选择的记录对象列表。也可以包含其他自定义数据。
  5. 渲染 QWeb 模板 (服务器端 - QWeb Engine):
    • Odoo 的 QWeb 引擎接收到:
      • ir.actions.report 中指定的 QWeb 模板 (template_id 指向的 ir.ui.view 记录)。
      • 步骤 4 中 Python 方法返回的数据字典作为渲染上下文。
    • QWeb 引擎解析 XML 模板,遍历数据(如 t-foreach="doc in docs"),评估条件 (t-if="..."),输出字段值 (t-field="...", t-esc="..."),调用子模板 (t-call="...") 等。
    • QWeb 引擎最终生成一个完整的 HTML 字符串。
  6. 生成最终文件 (服务器端 - wkhtmltopdf 或直接返回 HTML):
    • 如果 report_typeqweb-pdf:
      • Odoo 报表模块将步骤 5 生成的 HTML 字符串,连同 paperformat_id 指定的纸张格式配置(页边距、纸张大小、页眉页脚等),以及其他可能的 wkhtmltopdf 参数,传递给 wkhtmltopdf 命令行工具。
      • Odoo 服务器通过系统调用执行 wkhtmltopdf 命令,将 HTML 作为输入,指定输出为 PDF 文件。
      • wkhtmltopdf 进程运行,将 HTML 转换为 PDF 字节流。
      • Odoo 服务器捕获 wkhtmltopdf 的输出(PDF 字节流)。
    • 如果 report_typeqweb-html:
      • Odoo 服务器直接使用步骤 5 生成的 HTML 字符串作为报表输出。
  7. 发送文件响应 (服务器端 -> 客户端):
    • Odoo 服务器将生成的 PDF 字节流或 HTML 字符串封装在 HTTP 响应中。
    • 响应的 Content-Type 头会设置为相应的 MIME 类型(例如 application/pdftext/html)。
    • 响应通常还包含 Content-Disposition 头,建议客户端如何处理文件(例如 attachment; filename="report.pdf" 表示下载)。
    • 服务器将 HTTP 响应发送回客户端。
  8. 客户端处理文件 (客户端):
    • 客户端(Web 浏览器)接收到服务器的 HTTP 响应。
    • 根据 Content-TypeContent-Disposition 头,浏览器决定如何处理文件:通常是直接在浏览器中打开 PDF,或者提示用户下载文件。

3. 流程可视化 (分步说明)

+-------------------+     +-------------------+     +-----------------------+
| 1. 用户点击打印按钮 | --> | 2. 客户端发送RPC请求 | --> | 3. 服务器查找ir.actions.report |
| (Odoo Web UI)     |     | (触发动作)        |     | (根据ID/External ID)  |
+-------------------+     +-------------------+     +-----------------------+
                                     |
                                     v
+-----------------------+     +-----------------------+     +-----------------------+
| 4. 加载报表动作配置   | --> | 5. 调用Python方法准备数据 | --> | 6. QWeb引擎渲染HTML模板 |
| (report_type, model, |     | (_get_report_values)  |     | (结合数据与模板)      |
| template_id, etc.)    |     +-----------------------+     +-----------------------+
                                     |                                 |
                                     |                                 v
                                     |                     +-----------------------+
                                     |                     | 7a. 如果是qweb-pdf:   |
                                     |                     |     调用wkhtmltopdf   |
                                     |                     |     HTML -> PDF       |
                                     |                     +-----------------------+
                                     |                                 |
                                     |                     +-----------------------+
                                     |                     | 7b. 如果是qweb-html:  |
                                     |                     |     直接使用HTML      |
                                     |                     +-----------------------+
                                     |                                 |
                                     +---------------------------------+
                                     |
                                     v
+-----------------------+     +-----------------------+
| 8. 服务器发送文件响应 | --> | 9. 客户端处理文件     |
| (PDF或HTML)         |     | (下载/预览)         |
+-----------------------+     +-----------------------+

4. QWeb 模板引擎在报表渲染中的核心作用和机制

QWeb 是 Odoo 报表呈现层的核心。它的作用是将 Python 代码提供的数据,按照预定义的 HTML 结构和样式进行填充和排版,生成最终的 HTML 输出。

  • 核心机制: QWeb 模板是 XML/HTML 结构,其中嵌入了以 t- 开头的特殊属性(指令)。QWeb 引擎是一个解析器,它遍历模板树,识别这些指令,并根据指令和当前的数据上下文执行相应的操作。
  • 数据访问: Python 方法 (_get_report_values) 返回的字典中的数据,在 QWeb 模板中可以通过变量名直接访问。例如,如果 Python 返回 {'docs': records, 'company': current_company},在 QWeb 中就可以使用 docscompany 变量。最常用的变量是 docs,它包含了用户选择要打印的记录列表。在遍历 docs 时,通常使用 t-foreach="doc in docs",此时 doc 变量代表当前正在处理的记录。
  • 关键指令示例:
    • t-foreach="item in collection": 循环遍历集合。
    • t-if="condition": 条件判断,如果条件为真则渲染其内容。
    • t-field="record.field_name": 输出记录字段的值,并应用 Odoo 的格式化(如货币、日期)。
    • t-esc="variable": 输出变量的值,进行 HTML 转义。
    • t-raw="variable": 输出变量的值,不进行 HTML 转义(用于输出包含 HTML 标签的字符串)。
    • t-att="attribute_name, value": 设置元素的属性。
    • t-call="template_external_id": 调用另一个 QWeb 模板(常用于布局、页眉页脚)。
    • t-set="variable_name, value": 在模板中设置一个局部变量。
  • 布局: Odoo 提供了标准的外部布局 (web.external_layout) 和内部布局 (web.internal_layout) 模板。报表模板通常会使用 t-call="web.external_layout" 来包含标准的页眉、页脚和公司信息,确保报表风格一致。web.html_container 是最外层的容器模板,提供基本的 HTML 结构。

5. wkhtmltopdf 在 Odoo 报表生成中的角色、配置及其重要性

wkhtmltopdf 是 Odoo 生成 PDF 报表不可或缺的外部依赖。

  • 角色: 它的唯一职责是将 Odoo QWeb 引擎生成的 HTML 内容(包括 CSS 样式)精确地转换为 PDF 文档。Odoo 本身不包含一个完整的 HTML/CSS 渲染引擎和 PDF 生成库,因此依赖 wkhtmltopdf 来完成这个复杂的任务。
  • 重要性:
    • 高质量 PDF 输出: wkhtmltopdf 基于 WebKit 渲染引擎(与 Chrome/Safari 早期版本相似),能够较好地解析现代 HTML 和 CSS,生成视觉效果接近浏览器中显示的 PDF。
    • 处理复杂样式: 能够处理 CSS 盒模型、浮动、定位、字体、图片等,使得报表设计更加灵活和美观。
    • 页眉页脚和分页: wkhtmltopdf 支持通过命令行参数配置页眉、页脚、页码以及控制分页行为(尽管分页控制在 HTML/CSS 中实现起来可能比较复杂)。
  • 配置:
    • 安装: wkhtmltopdf 需要独立安装在运行 Odoo 服务器的机器上,并且 Odoo 用户需要有执行该命令的权限。
    • 路径: Odoo 需要知道 wkhtmltopdf 可执行文件的路径。通常,如果它在系统的 PATH 环境变量中,Odoo 可以直接找到。否则,可能需要在 Odoo 配置中指定路径(尽管这不常见,更推荐加入 PATH)。
    • 系统参数 report.url: 这是一个非常重要的配置。Odoo 在调用 wkhtmltopdf 时,通常不是直接传递 HTML 字符串,而是启动一个临时的 HTTP 服务器(或使用主 Odoo 实例的 /report/html/ 路由),让 wkhtmltopdf 通过 HTTP 请求获取 HTML 内容。report.url 系统参数(例如 http://localhost:8069)告诉 Odoo 报表模块 wkhtmltopdf 应该访问哪个 URL 来获取 HTML。这对于 wkhtmltopdf 正确加载 CSS、图片等相对路径资源至关重要。
    • 纸张格式 (report.paperformat): Odoo 中的 report.paperformat 模型允许用户配置纸张大小、方向、页边距、页眉页脚高度等。这些配置在生成 PDF 时会被 Odoo 报表模块读取,并作为命令行参数传递给 wkhtmltopdf
  • 潜在问题: wkhtmltopdf 的版本兼容性是一个常见问题,特别是页眉页脚的渲染。Odoo 官方通常推荐使用特定版本或经过 Odoo 补丁的版本,以确保最佳兼容性和稳定性。

6. ir.actions.report 的配置、属性及其关联

ir.actions.report 是 Odoo 报表架构的粘合剂,它定义了报表的所有元数据和行为。它是一个数据库模型 (ir.actions.report) 的记录。

  • 配置方式: 通常通过 XML 数据文件 (.xml) 在模块中定义。

    Sales Order
    sale.order
    qweb-pdf
    sale.report_saleorder
    sale.report_saleorder
    
    report
    
    
    
    
  • 关键属性:
    • name (Char): 报表动作的显示名称。
    • model (Char): 报表数据来源的主要 Odoo 模型的技术名称(例如 'sale.order')。
    • report_type (Selection): 报表输出类型,最常见的是 'qweb-pdf''qweb-html'
    • report_name (Char): 报表的内部名称,通常用于生成文件名,也是 Odoo 查找关联 QWeb 模板的默认名称(格式通常是 module_name.template_name)。
    • report_file (Char): 类似于 report_name,有时用于指定生成的文件名或查找模板。在现代 Odoo 版本中,report_namereport_file 通常设置为相同的值,并且与 QWeb 模板的外部 ID 相关联。
    • binding_model_id (Many2one to ir.model): 指定这个报表动作应该绑定到哪个模型上,以便在模型的视图中作为“打印”按钮出现。
    • binding_type (Selection): 指定绑定的类型,'report' 表示这是一个报表动作,会出现在“打印”菜单下。
    • paperformat_id (Many2one to report.paperformat): 关联一个纸张格式记录,用于配置 PDF 输出的纸张设置。
    • groups_id (Many2many to res.groups): 控制哪些用户组可以看到并执行这个报表动作。
    • template_id (Many2one to ir.ui.view): 显式关联用于渲染的 QWeb 模板视图。如果未指定,Odoo 会尝试根据 report_namereport_file 查找同名的 ir.ui.view 记录。
  • 关联到模板和模型:
    • model 属性指定了报表数据的主要来源模型,报表模块会调用该模型上的方法来获取数据。
    • template_id 或通过 report_name/report_file 隐式关联的 ir.ui.view 记录,指定了用于渲染报表的 QWeb 模板。这个模板定义了报表的结构和外观。
    • binding_model_id 属性将报表动作与特定的业务模型关联,使得用户可以在该模型的记录视图或列表视图中方便地访问打印功能。

7. 报表数据源(Python 模型)与 QWeb 模板之间的数据传递和交互方式

这是报表动态生成内容的关键环节。

  • 数据源: 报表的数据源是 ir.actions.reportmodel 属性指定的 Odoo Python 模型。
  • 数据准备方法: 报表模块在执行报表动作时,会查找并调用该模型上的 _get_report_values(self, docids, data=None) 方法。
    • self: 当前模型的实例。
    • docids: 一个列表,包含用户选择要打印的记录的数据库 ID。
    • data: 一个可选字典,可以包含从客户端或动作定义中传递的额外参数。
  • 数据传递: _get_report_values 方法必须返回一个字典。这个字典的键值对将成为 QWeb 模板渲染时的上下文变量。
    • 约定俗成: 返回字典中通常包含一个键为 'docs',其值为根据 docids 查询到的记录对象列表(例如 self.env[self.env.context.get('active_model')].browse(docids))。这是因为大多数报表都是针对一个或多个特定记录生成的。
    • 其他数据: 开发者可以在返回的字典中包含任何其他需要的数据,例如公司信息、计算的总计、配置参数等。
  • QWeb 中的数据访问:
    • 在 QWeb 模板中,可以直接通过返回字典中的键名访问对应的值。例如,如果返回 {'docs': records, 'total_amount': 1000},在 QWeb 中就可以使用 docstotal_amount 变量。
    • docs: 通常是一个记录集。可以使用 t-foreach="doc in docs" 来遍历每一条记录。
    • doc: 在 t-foreach="doc in docs" 循环内部,doc 变量代表当前正在处理的记录对象。可以像在 Python 中一样访问其字段和关联记录,例如 doc.name, doc.order_line, doc.partner_id.name
    • doc_model: 报表动作中指定的模型的技术名称字符串(例如 'sale.order')。
    • data: 如果 _get_report_values 方法接收并处理了 data 参数,并且将其包含在返回字典中,也可以在 QWeb 中访问。
    • 示例 (QWeb snippet):

    
        
            

Order:

Customer: ()

Total Amount:

  • - x

在这个例子中,docs 是从 Python 传递的记录集,doc 是循环中的当前记录,total_amount 是从 Python 传递的另一个变量。

8. 关键概念解释

  • 报表ID (Report ID): 指的是 ir.actions.report 数据库记录的内部唯一标识符 (id 字段)。这是一个整数,在同一个数据库中是唯一的。主要用于数据库内部关联和操作。
  • 外部ID (External ID): 也称为 XML ID。是一个模块名和标识符组成的字符串(例如 module_name.object_identifier)。它是在 XML 数据文件中定义记录时赋予的逻辑名称。外部 ID 的主要作用是在不同数据库实例之间(如开发、测试、生产环境)以及在模块升级时稳定地引用特定的数据库记录。在视图定义中触发报表动作时,通常使用报表动作的外部 ID (
  • 报表类型 (Report Type): ir.actions.report 记录的 report_type 字段。它决定了报表生成的方式和最终输出格式。
    • qweb-pdf: 使用 QWeb 渲染为 HTML,然后通过 wkhtmltopdf 转换为 PDF。这是最常见的类型。
    • qweb-html: 使用 QWeb 直接渲染为 HTML,并在浏览器中显示或作为 HTML 文件下载。
    • 早期版本还有其他类型(如 aeroo, rml),但在 Odoo 18 中,QWeb 是主要的内置报表类型。

9. Odoo 18 在打印架构上相较于早期版本可能存在的改进或变化

Odoo 的核心报表架构(ir.actions.report + QWeb + wkhtmltopdf)自 Odoo 8/9 以来一直保持相对稳定。Odoo 18 在这个基础上的改进更多是迭代和优化,而非颠覆性的改变。可能的改进方向包括:

  • 性能优化: 持续优化 QWeb 渲染速度,改进数据获取效率,或者更有效地调用 wkhtmltopdf
  • wkhtmltopdf 集成稳定性: 改进与不同版本 wkhtmltopdf 的兼容性,更好的错误处理和日志记录,尤其是在 wkhtmltopdf 调用失败时提供更清晰的反馈。
  • QWeb 功能增强: QWeb 引擎本身可能会增加新的指令或改进现有指令的功能,提供更灵活的模板设计能力。
  • CSS/HTML 兼容性: 随着 Web 标准的发展,Odoo 可能会更新其报表基础样式,以更好地兼容现代 CSS 特性,并在 wkhtmltopdf 中获得更好的渲染效果。
  • 用户界面改进: 报表相关的用户界面(如纸张格式配置、报表动作配置界面)可能会有所优化,使其更易用。
  • 替代 PDF 引擎的探索 (可能性较低): 虽然 wkhtmltopdf 仍然是主流,但社区或 Odoo 官方可能会持续关注或有限度地支持其他 PDF 生成方案,但这不太可能在 Odoo 18 中成为核心变化。

总的来说,Odoo 18 的报表架构是其成熟平台的一部分,开发者可以预期在现有知识基础上进行开发,同时受益于框架层面的性能和稳定性提升。

10. 总结

Odoo 18 的打印功能构建在一个稳定、灵活且可扩展的架构之上。它通过 ir.actions.report 统一管理报表行为,利用强大的 Python 模型层准备数据,依靠灵活的 QWeb 模板引擎定义报表布局和内容,并借助成熟的外部工具 wkhtmltopdf 生成高质量的 PDF 输出。

整个流程从用户界面触发动作开始,经过服务器端的动作识别、数据准备、QWeb 渲染,最终根据报表类型决定是直接返回 HTML 还是调用 wkhtmltopdf 生成 PDF,并将结果返回给客户端。

理解 报表ID外部ID报表类型 这些概念,掌握 ir.actions.report 的配置,熟悉 QWeb 模板的指令和数据交互方式,以及了解 wkhtmltopdf 的作用和配置,是进行 Odoo 报表开发和定制的关键。

尽管核心架构保持稳定,Odoo 18 在性能、兼容性和用户体验方面的持续优化,使得报表功能更加健壮和高效。作为技术架构师和开发者,深入理解这些底层机制,能够更有效地设计、开发和调试复杂的 Odoo 报表,满足多样的业务需求。


二、QWeb 报表设计与基础使用

QWeb 是 Odoo 框架中用于渲染 XML 模板的引擎,它在报表生成中扮演着至关重要的角色。报表设计师主要通过编写和修改 QWeb 模板来控制报表的布局、内容和样式。

1. QWeb 模板语言基础语法与指令

QWeb 模板本质上是带有特殊 t- 前缀属性的 XML/HTML 结构。这些 t- 属性就是 QWeb 指令,它们告诉 QWeb 引擎如何处理模板的这一部分。

基本结构:

一个典型的 QWeb 报表模板通常包含在一个 标签内,并经常调用 Odoo 提供的标准布局模板。


    
        
        

Report Title

  • : 这是最外层的容器,提供了基本的 HTML5 文档结构 (, , , )。
  • : 调用外部布局模板,它负责添加标准的页眉(公司 Logo、地址等)和页脚(页码、公司信息等)。如果需要自定义页眉页脚或不需要标准布局,可以使用 web.internal_layout 或完全不调用布局模板。
  • : 在布局模板内部,报表的主体内容通常放在一个带有 page 类的 div 中,这有助于控制分页和样式。

核心 QWeb 指令:

以下是一些最常用的 QWeb 指令及其在报表中的应用:

  • t-if="condition": 条件渲染。如果 condition 为真,则渲染包含 t-if 属性的元素及其内容;否则,跳过渲染。

This is a confirmed sale order.

Large Order Discount Applied!

    • 解释: 根据 Python 模型中传递的数据(例如 doc 对象的 stateamount_total 字段值)来决定是否显示某个段落或元素。
  • t-foreach="collection" t-as="variable_name": 循环遍历集合。常用于遍历记录集(如订单行、发票行)。

            
Product Quantity Price
    • 解释: 遍历 doc 对象(例如一个销售订单记录)的 order_line 关联记录集。在每次循环中,当前订单行记录被赋值给 line 变量,然后在循环体内部可以使用 line 来访问订单行的字段。
  • t-field="record.field_name": 显示记录字段的值,并应用 Odoo 的默认格式化。这是显示模型字段值的首选方式。

Order Date:

Customer:

Total:

    • 解释: Odoo 会根据字段类型(日期、数字、货币、关联字段等)自动选择合适的格式化方式。例如,日期字段会根据用户偏好格式化,货币字段会显示货币符号和正确的精度。
  • t-esc="variable": 显示变量的值,并进行 HTML 转义。用于显示非字段数据或需要确保安全输出的字符串。

Report Generated By:

Custom Message:

    • 解释: 如果 user.name 是 "Admin alert('xss') ",t-esc 会将其转义为 "Admin ",防止跨站脚本攻击。
  • t-raw="variable": 显示变量的值,不进行 HTML 转义。用于显示包含 HTML 标签的字符串(例如富文本字段的内容)。
    • 解释: 如果 doc.description_sale 字段包含

      Hello World

      t-raw 会直接输出这些 HTML 标签,浏览器会将其渲染为格式化的文本。
  • t-set="variable_name, value": 在模板中定义一个局部变量。这个变量只在定义它的元素及其子元素范围内有效。

Total Quantity of Products:

    • 解释: 计算所有订单行的产品数量总和,并将其存储在名为 total_qty 的变量中,然后在模板的其他地方使用它。t-value 属性用于指定变量的值,可以使用 Python 表达式。
  • t-call="template_external_id": 调用并渲染另一个 QWeb 模板,并将其输出插入到当前位置。常用于模块化报表设计,将页眉、页脚、地址块等独立成子模板。
    • 解释: 渲染外部 ID 为 my_module.report_address_block 的 QWeb 模板,并将其内容放在这里。
  • t-att="attribute_name, value": 动态设置元素的属性。

Draft Order
    • 解释: 第一个例子动态设置 标签的 src 属性,用于显示公司 Logo 图片(通常存储为 base64 编码)。第二个例子根据订单状态动态设置 divclass 属性。t-att-attribute_namet-att="attribute_name, value" 的简写形式。
  • t-options='{"option": value, ...}':t-field 结合使用,提供额外的格式化选项。

Price:

Date:

    • 解释: 第一个例子强制使用货币控件格式化 amount_total,并指定使用 doc.currency_id 作为货币符号。第二个例子强制使用日期控件格式化 date_order

2. 通过 Odoo 开发者模式访问和修改现有报表模板

在 Odoo 中,报表模板是以“视图” (ir.ui.view) 的形式存储在数据库中的。开发者模式允许你直接在 Web 界面中查找和编辑这些视图。

步骤:

  1. 启用开发者模式:
    • 登录 Odoo。
    • 点击右上角的用户头像 -> "About Odoo"。
    • 在弹出的窗口中,点击 "Activate the developer mode" 或 "Activate the developer mode (with assets)". 后者在调试前端问题时更有用。
    • 或者,在当前 URL 中添加 #debug=1#debug=assets
  2. 导航到视图列表:
    • 启用开发者模式后,顶部菜单栏会出现 "Technical" (技术) 菜单。
    • 点击 "Technical" -> "User Interface" (用户界面) -> "Views" (视图)。
  3. 查找报表模板:
    • 在视图列表中,你可以通过搜索来找到特定的报表模板。
    • 报表模板的名称通常与其关联的 ir.actions.reportreport_namereport_file 属性相关。例如,销售订单报表的模板名称可能是 sale.report_saleorder_document 或包含 sale.report_saleorder
    • 你也可以通过搜索视图的“类型” (Type) 为 qweb 来过滤。
    • 找到目标模板后,点击进入编辑页面。
  4. 修改模板:
    • 在视图编辑页面,你可以看到模板的 XML 代码。
    • 直接在 "Architecture" (架构) 字段中修改 QWeb/HTML 代码。
    • 修改完成后,点击 "Save" (保存)。
  5. 测试修改:
    • 回到相应的业务记录(例如一个销售订单)。
    • 点击打印按钮,生成报表。
    • 查看生成的报表,确认修改是否生效。

注意: 直接在开发者模式下修改视图会立即生效,但这不推荐作为长期或生产环境的修改方式。这些修改会直接写入数据库,并且在模块升级时可能会丢失或与模块自带的视图定义冲突。最佳实践是创建一个自定义模块,使用 QWeb 模板继承来修改现有模板。

3. 常见报表元素在 QWeb 中的实现

  • 表格: 使用标准的 HTML , , , ,
    , 标签,结合 t-foreach 遍历数据行。
    
                
    Description Quantity Unit Price Amount
    • 图片: 使用 标签。对于存储在 Odoo 字段中的图片(如公司 Logo),通常是 base64 编码的,需要使用 t-att-src 动态设置 src 属性。
    
    
    
    
    
      • to_text() 是一个 QWeb 辅助函数,用于确保 base64 字符串是文本格式。
    • 页眉页脚和页码: 如前所述,这主要通过调用 web.external_layoutweb.internal_layout 实现。这些布局模板内部使用了特定的 HTML 结构和 CSS 类(如 header, footer, page-break-after: always;)以及 wkhtmltopdf 的功能来处理页眉页脚和自动页码。
      • 页码通常由 wkhtmltopdf 在生成 PDF 时自动插入,布局模板提供了插入页码的位置。
      • 如果你需要完全自定义页眉页脚,可以不调用标准布局,自己编写

    4. 数据绑定:Python 数据到 QWeb 模板

    数据绑定是 QWeb 报表的核心。Python 模型中的 _get_report_values(self, docids, data=None) 方法返回的字典,其键值对会直接映射到 QWeb 模板的渲染上下文中。

    • Python 方法示例 (sale.order 模型):
    class SaleOrder(models.Model):
        _inherit = 'sale.order'
    
        def _get_report_values(self, docids, data=None):
            # 调用父类方法获取标准数据 (通常包含 'docs')
            report_values = super()._get_report_values(docids, data)
    
            # 获取当前用户
            current_user = self.env.user
    
            # 计算总订单行数
            total_lines = sum(len(order.order_line) for order in report_values['docs'])
    
            # 添加自定义数据到字典
            report_values.update({
                'current_user': current_user,
                'total_lines_count': total_lines,
                'custom_message': "Thank you for your business!",
            })
            return report_values
    • QWeb 模板中的数据访问:

    在 QWeb 模板中,你可以直接使用 Python 方法返回字典中的键名作为变量名。

    
        
             
                

    Order:

    Salesperson:

    Report generated by:

    Total number of lines across all selected orders:

      • docs: 包含用户选择的记录列表,通常是 _get_report_values 方法中根据 docids 查询得到的记录集。
      • doc: 在 t-foreach="doc in docs" 循环中,代表当前正在处理的记录对象。
      • doc_ids: 传递给 _get_report_values 的原始记录 ID 列表。
      • doc_model: 报表动作中指定的模型的技术名称字符串。
      • user: 当前登录的用户记录。
      • company: 当前用户所属的公司记录。
      • report_type: 报表类型字符串(如 'qweb-pdf')。
      • data: 如果 _get_report_values 方法接收并返回了 data 参数,也可以在 QWeb 中访问。
      • 以及你在 _get_report_values 返回字典中添加的任何其他键值对。

    5. 应用 CSS 样式美化报表

    QWeb 模板渲染为 HTML,因此你可以使用标准的 CSS 来控制报表的布局、字体、颜色、边距等。

    • Odoo 报表默认样式: Odoo 的标准布局模板 (web.external_layout, web.internal_layout) 会自动包含 Odoo 报表模块提供的默认 CSS 样式。这些样式定义了基础的字体、表格样式、页边距等,使得报表具有一致的外观。这些样式通常位于 addons/web/static/src/css/report.css 或类似的路径下。
    • 添加自定义 CSS:
      • 内联样式: 直接在 HTML 元素的 style 属性中编写 CSS。适用于少量、特定的样式调整。
    Urgent!
      • 内部样式表: 在模板的 部分(如果自己编写了完整的 HTML 结构)或在模板的任何位置使用
            • 注意: 如果调用了标准布局, 部分通常由布局模板提供。你可以在继承布局模板时添加自己的