报表
报表打印
Odoo 8.0 开始使用新的基于QWeb,Twitter Bootstrap 和 Wkhtmltopdf的报表引擎。一个报表有两个元素组成:
其一是ir.actions.report.xml
中,针对报表提供的快捷元素<report>
,这个元素用于设置报表的各种基本参数(如默认类型,报表生成后是否保存到数据库)
<report
id="account_invoices"
model="account.invoice"
string="Invoices"
report_type="qweb-pdf"
name="account.report_invoice"
file="account.report_invoice"
attachment_use="True"
attachment="(object.state in ('open','paid')) and
('INV'+(object.number or '').replace('/','')+'.pdf')"
/>
另一个是标准的QWeb视图,实例如下:
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="report.external_layout">
<div class="page">
<h2>Report title</h2>
</div>
</t>
</t>
</t>
the standard rendering context provides a number of elements, the most
important being:
``docs``
the records for which the report is printed
``user``
the user printing the report
由于报表是标准的web页面,所以都可以通过URL和输出参数来访问,例如HTML版本的发票报表可以通过这个地址访问:http://localhost:8069/report/html/account.report_invoice/1(如果安装了account
),而PDF版本可以通过这个地址访问:http://localhost:8069/report/pdf/account.report_invoice/1。
警告
如果出现了PDF报表没有样式(比如有文本显示但是与html版本的风格/布局不一致),可能是wkhtmltopdf进程不能为下载提供web服务。
如果你检查服务日志,并且看到生成PDF报表时CSS样式未被下载,那么就可以肯定是这个问题。
wkhtmltopdf进程使用web.base.url
这个系统参数作为根路径来组合所有文件路径,但是这个参数是在管理员帐户登录后自动生成的。如果你的服务器位于某种代理服务器之后,则无法访问该服务器。你可以添加一个系统参数来固化它:
-
report.url
,可以访问你的服务器的URL地址,比如http://localhost:8069或者类似的地址。这个参数仅用于这个特殊情况。 -
web.base.url.freeze
,当设置这个参数值为True
时将停止自动更新web.base.url
。
练习创建授课模型的报表
对于每个授课,报表都将显示它的授课名称,开始和结束时间,以及课程出席人列表。
openacademy / __ manifest__.py
'views/openacademy.xml',
'views/partner.xml',
'views/session_workflow.xml',
'reports.xml',
],
# only loaded in demonstration mode
'demo': [
openacademy/reports.xml
<odoo>
<data>
<report
id="report_session"
model="openacademy.session"
string="Session Report"
name="openacademy.report_session_view"
file="openacademy.report_session"
report_type="qweb-pdf" />
<template id="report_session_view">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="report.external_layout">
<div class="page">
<h2 t-field="doc.name"/>
<p>From <span t-field="doc.start_date"/> to <span t-field="doc.end_date"/></p>
<h3>Attendees:</h3>
<ul>
<t t-foreach="doc.attendee_ids" t-as="attendee">
<li><span t-field="attendee.name"/></li>
</t>
</ul>
</div>
</t>
</t>
</t>
</template>
</data>
</odoo>
仪表盘
练习定义一个仪表盘
定义一个仪表盘,这个仪表盘包含了已经建立的图形视图、授课日历视图、课程列表视图(可以选择form视图)。这个仪表盘可以通过菜单中的菜单项使用,并且当选择开放学院主菜单时自动显示在web客户端。
- 建立文件
openacademy/views/session_board.xml
。这个文件包含面板视图,视图的触发action,打开仪表盘的action,以及重新定义主菜单项以添加仪表盘的action。
注意
可以使用的仪表盘风格有:1
,1-1
,2-1
和1-1-1
- 更新
openacademy/__manifest__.py
文件,以引用新的数据文件。
openacademy/__manifest__.py
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base', 'board'],
# always loaded
'data': [
'views/openacademy.xml',
'views/partner.xml',
'views/session_workflow.xml',
'views/session_board.xml',
'reports.xml',
],
# only loaded in demonstration mode
openacademy/views/session_board.xml
<?xml version="1.0"?>
<odoo>
<data>
<record model="ir.actions.act_window" id="act_session_graph">
<field name="name">Attendees by course</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">graph</field>
<field name="view_id"
ref="openacademy.openacademy_session_graph_view"/>
</record>
<record model="ir.actions.act_window" id="act_session_calendar">
<field name="name">Sessions</field>
<field name="res_model">openacademy.session</field>
<field name="view_type">form</field>
<field name="view_mode">calendar</field>
<field name="view_id" ref="openacademy.session_calendar_view"/>
</record>
<record model="ir.actions.act_window" id="act_course_list">
<field name="name">Courses</field>
<field name="res_model">openacademy.course</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<record model="ir.ui.view" id="board_session_form">
<field name="name">Session Dashboard Form</field>
<field name="model">board.board</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Session Dashboard">
<board style="2-1">
<column>
<action
string="Attendees by course"
name="%(act_session_graph)d"
height="150"
width="510"/>
<action
string="Sessions"
name="%(act_session_calendar)d"/>
</column>
<column>
<action
string="Courses"
name="%(act_course_list)d"/>
</column>
</board>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="open_board_session">
<field name="name">Session Dashboard</field>
<field name="res_model">board.board</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="usage">menu</field>
<field name="view_id" ref="board_session_form"/>
</record>
<menuitem
name="Session Dashboard" parent="base.menu_reporting_dashboard"
action="open_board_session"
sequence="1"
id="menu_board_session" icon="terp-graph"/>
</data>
</odoo>
WebServices
WebService模型为所有web服务提供通用接口:
- XML-RPC
- JSON-RPC
业务对象也可以通过这种分布式机制进行访问,在客户端界面通过上下文编辑这些业务对象。
Odoo支持通过XML-RPC/JSON-RPC接口访问,这两种协议在许多开发语言中都有库支持。
XML-RPC库
下面的例子是Python程序通过xmlrpclib
库访问Odoo服务器:
import xmlrpclib
root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
uid = xmlrpclib.ServerProxy(root + 'common').login(DB, USER, PASS)
print "Logged in as %s (uid: %d)" % (USER, uid)
# Create a new note
sock = xmlrpclib.ServerProxy(root + 'object')
args = {
'color' : 8,
'memo' : 'This is a note',
'create_uid': uid,
}
note_id = sock.execute(DB, uid, PASS, 'note.note', 'create', args)
练习在客户端添加新服务
编写Python程序,用来发送XML-RPC请求到运行Odoo的PC。这个程序将显示全部授课,以及所对应的座位数。也可以为课程创建新的授课。
import functools
import xmlrpclib
HOST = 'localhost'
PORT = 8069
DB = 'openacademy'
USER = 'admin'
PASS = 'admin'
ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)
# 1. Login
uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS)
print "Logged in as %s (uid:%d)" % (USER,uid)
call = functools.partial(
xmlrpclib.ServerProxy(ROOT + 'object').execute,
DB, uid, PASS)
# 2. Read the sessions
sessions = call('openacademy.session','search_read', [], ['name','seats'])
for session in sessions:
print "Session %s (%s seats)" % (session['name'], session['seats'])
# 3.create a new session
session_id = call('openacademy.session', 'create', {
'name' : 'My session',
'course_id' : 2,
})
不使用硬编码,通过课程名称查找课程ID:
# 3.create a new session for the "Functional" course
course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]
session_id = call('openacademy.session', 'create', {
'name' : 'My session',
'course_id' : course_id,
})
JSON-RPC库
下面的例子是一个Python程序,通过Pythong的标准库urllib2
和json
与Odoo服务器交互。
import json
import random
import urllib2
def json_rpc(url, method, params):
data = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": random.randint(0, 1000000000),
}
req = urllib2.Request(url=url, data=json.dumps(data), headers={
"Content-Type":"application/json",
})
reply = json.load(urllib2.urlopen(req))
if reply.get("error"):
raise Exception(reply["error"])
return reply["result"]
def call(url, service, method, *args):
return json_rpc(url, "call", {"service": service, "method": method, "args": args})
# log in the given database
url = "http://%s:%s/jsonrpc" % (HOST, PORT)
uid = call(url, "common", "login", DB, USER, PASS)
# create a new note
args = {
'color' : 8,
'memo' : 'This is another note',
'create_uid': uid,
}
note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'create', args)
下面是使用jsonrpc
完成相同功能的程序
import jsonrpclib
# server proxy object
url = "http://%s:%s/jsonrpc" % (HOST, PORT)
server = jsonrpclib.Server(url)
# log in the given database
uid = server.call(service="common", method="login", args=[DB, USER, PASS])
# helper function for invoking model methods
def invoke(model, method, *args):
args = [DB, uid, PASS, model, method] + list(args)
return server.call(service="object", method="execute", args=args)
# create a new note
args = {
'color' : 8,
'memo' : 'This is another note',
'create_uid': uid,
}
note_id = invoke('note.note', 'create', args)
提醒
有许多语言可以通过XML-RPC或JSON-RPC访问Odoo系统的高级API,例如: