創建商業報表

通常我們會在我們的To Do模塊中實現報表。但是爲了學習目的,我們將爲我們的報表創建一個新的addon模塊。

我們的報表效果如下:

我們將命名這個新的addon模塊爲todo_report。首先要做的是創建一個空的__init__.py文件和__manifest__.py文件:

(

'name': 'To–Do Report',

'deion': 'Report for To–Do tasks.', 'author': 'Daniel Reis',

'depends': ['todo_kanban'],

'data': ['reports/todo_report.xml'] }

reports/todo_report.xml文件可以通過聲明以下內容開始:

<?xml version="l.O"?>

<odoo>

<report id="action_todo_task_report" string="To–do Tasks" model="todo.task" report_type="qweb–pdf"

name="todo_report.report_todo_task_template"

/>

</odoo>

<report>標籤是將數據寫入到ir.actions.report.xml模型的快捷方式。這個模型代表一種特定類型的客戶端動作。可以在Settings | Technical | Reports菜單選項中瀏覽這個模型的數據。

在報告的設計過程中,最好先設置報表類型report_type="qweb–html",並在完成報表設計後將其修改回qweb-pdf文件。這樣可以更快地生成報表,也更容易檢查來自OWeb模板的HTML結果。

在安裝了這個之後,待辦任務表單視圖將在頂部在More按鈕的左邊,顯示一個Print按鈕,其中包含執行報表打印的選項。

因爲我們還沒有定義報表,所以現在無法使用。這將是一個QWeb報表,因此它需要一個QWeb模板。name屬性可以標識將使用的模板。與其他標識符引用不同,name屬性中的模塊前綴是必需的。我們必須充分參考<module_name>.<identifier_name>。

QWeb報表模板

報表通常遵循基本框架,如下所示。這可以添加到reports/todo_report.xml文件,在<report>元素之後。

<template id="report_todo_task_template">

<t t–call="report.html_container">

<t t–call="report.external_layout">

<div class="page">

<!–– Report page content ––>

</div>

</t>

</t>

</template>

這裏最重要的元素是使用標準報表結構的t-call指令。report.html_container模板將運行支持HTML文檔的基本設置。report.external_layout模板,將使用公司的相應設置,處理報表頁眉和頁腳。作爲另一種選擇,我們可以使用report.internal_layout模板,它只使用一個基本頁眉。

現在,我們有了模塊和報表視圖的基本框架。請注意,由於報表是QWeb模板,所以就像其他視圖一樣可以使用繼承。在報表中使用的QWeb模板,可以使用XPATH表達式進行常規的繼承視圖擴展。

報表中的數據

與看板視圖不同,報表中的QWeb模板是在服務器端渲染的,並使用Python QWeb實現。我們可以將此視爲同一規範的兩種實現,故我們需要了解其中的差異。

首先,使用Python語法對QWeb表達式進行計算,而不是使用Java。對於最簡單的表達式,可能很少或沒有區別;但是若是更復雜的操作,表達式可能會有所不同。

表達式的計算方法也不同。對於報表,我們有以下變量:

• docs是一個可重複的集合,其中包含要打印的記錄。

• doc_id是要打印的記錄的ID列表。

• doc_model標識記錄的模型,例如todo.task。

• time是對Python時間庫的引用。

• user是運行報告的用戶的記錄。

• res_company是當前用戶公司的記錄。

報表內容是用HTML編寫的,字段值可以使用t–field屬性引用。可以用t–field–options屬性來進行補充,效果是以特定的小部件來渲染字段內容。

現在我們可以開始爲我們的報表設計頁面內容了:

<!–– Report page content

<div class="row bg–primary">

<div class="col–xs–3">

<span class="glyphicon glyphicon–pushpin" /> What

</div>

<div class="col–xs–2">Who</div>

<div class="col–xs–l">When</div>

<div class="col–xs–3">Where</div>

<div class="col–xs–3">Watchers</div>

</div>

<t t–foreach="docs" t–as="o">

<div class="row">

<!-- Data Row Content -->

</div>

</t>

內容的佈局可以使用Twitter Bootstrap HTML網格系統。簡單地說,Bootstrap有一個包含12個列的網格佈局。可以使用<div class="row">添加新的行。在一行中,我們有單元格,每一個都跨越一定數量的列,總計應當佔據12列。每個單元格都可以用<div class="col–xs–N">來定義,其中N是它跨越的列數。

可以在http://getbootstrap.com上找到對Bootstrap的完整引用,描述上面介紹過的以及更多的樣式元素。

在這裏,我們添加標題行和標題,然後我們有一個t-foreach循環,遍歷每個記錄,併爲每個記錄渲染一行。

由於渲染是在服務器端完成的,所以記錄是對象,我們可以使用點表示法從相關數據記錄訪問字段。這使得通過關係字段來訪問它們的數據變得很容易。請注意,這在客戶端渲染的Qweb、視圖(如web客戶端看板視圖)中是不可能的。

以下XML可以渲染記錄行的內容:

<div class="col–xs–3">

<h4><span t–field="o.name" /></h4>

</div>

<div class="col–xs–2">

<span t–field="o.user_id" />

</div>

<div class="col–xs–l">

<span t–field="o.date_deadline" />

<span t–field="o.amount_cost" t–field–options='(

"widget": "monetary", "display_currency": "o.currency_id"}'/>

</div>

<div class="col–xs–3">

<div t–field="res_company.partner_id" t–field–options='(

"widget": "contact",

"fields": ["address", "name", "phone", "fax"], "no_marker": true}' />

</div>

<div class="col–xs–3">

<!–– Render followers ––>

</div>

如我們所見,字段使用時可附帶額外選項。這些非常類似於表單視圖中使用的options屬性。在第6章,視圖-設計用戶界面(Views – Designing the User Interface),使用附加widget來設置渲染字段的小部件。

一個例子是上面使用的貨幣小部件,緊挨着截止日期。

一個更復雜的示例是用於格式化地址的contact小部件。我們使用了公司地址res_company.partner_id,並且因爲它有一些默認數據,我們可以立即看到渲染出的地址。但是,使用當前用戶的地址,o.user_id.partner_id更有意義一些。默認情況下,contact小部件顯示帶有一些圖標的地址,例如一個電話圖標。我們使用的no_marker="true"選項禁用了它們。

渲染圖片

我們報表的最後一部分將以帶頭像的關注者列表結束。我們將使用Bootstrap組件media–list,並循環關注者列表來渲染其中的每一個人:

<!–– Render followers ––>

<ul class="media–list">

<t t–foreach="o.message_follower_ids" t–as="f">

<li t–if="f.partner_id.image_small" class="media–left">

<img class="media–object" t–att–src="'data:image/png;,%s' %

f.partner_id.image_small" style="max–height: 24px;" />

<span class="media–body" t–field="f.partner_id.name" />

</li>

</t>

</ul>

二進制字段的內容以形式提供。<img>元素的src屬性可以直接使用這類數據。因此,我們可以使用QWeb指令 t-att-src來動態生成每個圖像。

彙總總數和累計總數

報表中經常需要提供總數。可以使用Python表達式來計算這些總數。

在<t t–foreach>標籤結束之後,我們來添加用於計算總數的最後一行:

<!–– Totals ––>

<div class="row">

<div class="col–xs–3">

Count: <t t–esc="len(docs)" />

</div>

<div class="col–xs–2" />

<div class="col–xs–l"> Total:

<t t–esc="sum([o.amount_cost for o in docs])" />

</div>

<div class="col–xs–3" />

<div class="col–xs–3" />

</div>

len() Python語句用於計算集合中元素的數量。總數可以用sum()在一個值列表上計算。在前面的示例中,我們使用一個Python列表推導式從docs記錄集生成一個值列表。您可以將列表推導式看作一個嵌入式的for循環。

有時我們想在報告中執行一些計算。例如,一個累計總數(循環到當前記錄時的總和)。這可以用t-set來定義一個累加變量,然後在每一行上更新它。

爲了說明這一點,我們可以試着計算關注者的累計數量。我們應該在循環docs記錄集的t-foreach之前,初始化這個變量:

<t t–set="follower_count" t–value="0" />

然後,在循環中,將變量每次都將加上當前記錄的關注者數量。我們選擇在渲染出關注者列表後執行這個指令,並在每一行打印出當前的總數:

<!–– Running total––>

<t t–set="follower_count"

t–value="follower_count + len(o.message_follower_ids)" /> Accumulated # <t t–esc="follower_count" />

定義紙張格式

現在,我們的報表在HTML格式中看起來不錯,但是它在PDF頁面上並沒有很好地打印出來。我們可以使用橫向頁面獲得更好的結果。所以我們需要添加這種紙張格式。

在XML文件的頂部,添加以下記錄:

<record id="paperformat_euro_landscape" model="report.paperformat">

<field name="name">European A4 Landscape</field>

<field name="default" eval="True" />

<field name="format">A4</field>

<field name="page_height">0</field>

<field name="page_width">0</field>

<field name="orientation">Landscape</field>

<field name="margin_top">40</field>

<field name="margin_bottom">23</field>

<field name="margin_left">7</field>

<field name="margin_right">7</field>

<field name="header_line" eval="False" />

<field name="header_spacing">35</field>

<field name="dpi">90</field>

</record>

它是一份歐洲A4格式的拷貝,源代碼在addons/report/data, report_paperformat.xml文件中定義。但我們將紙張方向由縱向改到橫向。從web客戶端可以看到定義的文件格式,Settings | Technical | Reports | Paper Format.。

現在我們可以在報表中使用它了。默認的文件格式是在公司設置中定義的,但是我們也可以指定特定報表使用的紙張格式。這是在報表操作中使用paperfomat屬性完成的。

讓我們編輯用於打開報告的動作,並添加此屬性:

<report id="action_todo_task_report" string="To–do Tasks" model="todo.task" report_type="qweb–pdf"

name="todo_report.report_todo_task_template" paperformat="paperformat_euro_landscape"

/>

在9.0版本中添加了 <report>標籤的paperformat屬性。對於8.0,我們需要使用<record>元素來添加具有paperformat值的報表動作。

在報表中使用翻譯

要爲報表啓用翻譯,需要使用一個模板從模板調用它。這裏使用的是帶有t-lang屬性的<t t–call>元素。

t-lang屬性應該賦值爲一個語言代碼,比如es或en_US。它需要一個含有語言字段的記錄。我們經常使用的是接收這張報表的合作伙伴的語言字段。客戶的語言設置存儲在partner_id.lang字段。在我們的例子中,我們沒有合作伙伴字段,但是我們可以使用負責用戶的語言,而相應的語言首選項是user_id.lang。

該函數需要傳入一個模板名稱,並將渲染和翻譯它。這意味着我們需要在一個單獨的模板中定義報告的頁面內容,如下所示:

<report id="action_todo_task_report_translated" string="Translated To–do Tasks" model="todo.task"

report_type="qweb–pdf" name="todo_report.report_todo_task_translated" paperformat="paperformat_euro_landscape"

/>

<template id="report_todo_task_translated">

<t t–call="todo_report.report_todo_task_template" t–lang="user.lang" >

<t t–set="docs" t–value="docs" />

</t>

</t>

</template>

基於自定義SQL的報表

我們所建的報表是建立在常規記錄的基礎上的。但是在某些情況下,我們需要轉換或聚合數據。在渲染報表時,處理動態數據並不是一件容易的事情。

其中一種方法是編寫一個SQL查詢來構建我們需要的數據集,然後通過一個特殊的模型存儲這些結果,並根據獲得記錄集進行報表工作。

爲此,我們將創建一個reports/todo_task_report.py文件,使用代碼如下:

# –*– coding: utf–8 –*–

from odoo import models, fields

class TodoReport(models.Model):

_name = 'todo.task.report'

_deion = 'To–do Report'

_sql = """

CREATE OR REPLACE VIEW todo_task_report AS SELECT *

FROM todo_task WHERE active = True """

name = fields.Char('Deion') is_done = fields.Boolean('Done?') active = fields.Boolean('Active?')

user_id = fields.Many2one('res.users', 'Responsible') date_deadline = fields.Date('Deadline')

要加載這個文件,我們需要添加一個from . import reports行到 init .py文件的頂部,同時添加from . import todo_task_report到reports/ init .py文件。

sql屬性用於覆蓋數據庫表的自動創建特性,同時提供一個SQL語句。我們希望它創建一個數據庫視圖來提供報告所需的數據。我們的SQL查詢實際上非常簡單,但關鍵是此時我們可以使用任何有效的SQL查詢來生成我們的視圖。

我們還映射了我們需要的ORM字段類型的字段,以便它們可以在這個模型上生成的記錄集上使用。

接下來,我們可以根據這個模型添加一個新的報表,reports/todo_model_report.xml:

<odoo>

<report id="action_todo_model_report" string="To–do Special Report" model="todo.task" report_type="qweb–html"

name="todo_report.report_todo_task_special"

/>

<template id="report_todo_task_special">

<t t–call="report.html_container">

<t t–call="report.external_layout">

<div class="page">

<!–– Report page content ––>

<table class="table table–striped">

<tr>

<th>Title</th>

<th>Owner</th>

<th>Deadline</th>

</tr>

<t t–foreach="docs" t–as="o">

<tr>

<td class="col–xs–6">

<span t–field="o.name" />

</td>

<td class="col–xs–3">

<span t–field="o.user_id" />

</td>

<td class="col–xs–3">

<span t–field="o.date_deadline" />

</td>

</tr>

</t>

</table>

</div>

</t>

</t>

</template>

</odoo>

對於更復雜的情況,我們可以使用一個不同的解決方案:嚮導。爲此,我們應該創建一個具有相關字段的暫態模型,其中頭部包含了由用戶引入的報告參數,並且這些字段會將查詢出的數據提供給報告使用。這些行是由一個模型方法生成的,它可以包含我們可能需要的任何邏輯。強烈建議從現有的類似報告中獲取靈感。

小結

在前一章中,我們學習了QWeb,以及如何使用它來設計看板視圖。在本章中,我們瞭解了QWeb報告引擎,以及使用QWeb模板語言構建報告時最重要的技術。

在下一章中,我們將繼續使用QWeb,並構建網站頁面。我們還將學習編寫web控制器,爲我們的web頁面提供更豐富的特性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章