鉴于OpenERP的架构,它不适合直接通过PostgreSQL客户端或者 ODBC 这样的连接方法访问数据库, 幸运的是,OpenERP提供了一个非常全面的web服务集,允许你通过标准协议做任何事情。
Note
虽然直接访问数据库,在技术上是可行的,你必须意识到这可能对您的数据造成灾难性的后果,除非你知道你是在做 什么。当你直接访问数据库的时候,建议您关闭OpenERP服务器,以避免缓存和并发问题。
目前支持的协议是XML-RPC和Net-RPC。XML-RPC是用于Web服务的第一批标准之一, 几乎可以再任何语言中使用。这是一个非常详细的协议,而且在需要的时候可以引入潜在位。 另一方面,Net-RPC是一个优化的协议,特别用在Python编写的应用程序之间。.
对于REST风格的web服务的支持在将来的OpenErp发布包中支持。
对于SOAP协议,目前的OPenErp已经不再支持,但是如果在社区有足够的爱好者的话将来可能恢复。
OpenERP 为你提供了以下的Web服务.
Note
你能在服务的源码(/bin/service/web_services.py)的对应类里面找到每种服务的细节 .
db:
提供函数创建、删除、备份、恢复数据库. 请谨慎使用!
common:
让你登录和退出 OpenERP, 并且提供各种实用功能。你只有登录后才能使用其他的网络服务.
object:
这是最有用的网络服务,因为通过它可以访问 OpenERP 对象. 值得注意的是, 函数 “execute” 让你调用对象的方法, 比如可以搜索的大部分的ORM方法,读写记录。它也可以用来调用价格计算等对象的其他方法.
Note
主要的 ORM 方法一览:
通过 Web 服务不能使用 Browse() 函数.
另一个有用的功能是 “exec_workflow”, 它可以让你通过工作流制定记录的进展.
向导:
提供对旧式的向导。新风格的向导是基于ORM的,因此他们可以通过 “object” web 服务来进行访问.
报告:
让你生成和检索报告.
下面是一个写数据的例子程序。在下一章你会发现关于多种编程语言 XML-RPC的更详尽的例子.
login: 在Web服务 “common” 中调用 “login” 函数,使用下面的参数:
- database
- user name
- password
创建一个新的合作者: 在Web服务 “object” 中调用 “execute” 函数,使用下面的参数:
- database
- user id provided by “login” in step 1.
- the object name : ‘res.partner’
- the name of the ORM method : “create”
- some data to be recorded
上面提到的数据都是键值对, 比如:
- name: Fabien Pinckaers
- lang: fr_FR
但是更复杂的数据结构也可以发送。比如你可以在一个单一的Web服务调用中创建一个合作者 和他的地址。在那种情况下,所有的数据在服务的相同的数据库事务中来处理。这意味. 着你一定要保存好你的数据一致性的状态。这是对所有ERP应用的关键要求.
XML-RPC 是一个著名的Web服务. Web 服务是一个工具,它可以再现有的网络基础设施上面设置分布式的应用程序。这些应用程序使用一种传输层的网络但是并不提供直接通过浏览器的人机界面。可扩展标记语言(XML)提供了描述远程过程调用(RPC)的词汇表,RPC是使用超文本传输协议(HTTP) 在计算机之间传输。实际上,RPC让各开发者自行定义网络调用中的接口。这些接口可以是很简单的一个函数调用也可以像大型API那样复杂.
XML-RPC 允许在两台或者更多运行不同操作系统和不同语言程序的计算机之间协同处理。比如,一个JAVA应用可以和一个Perl应用会谈,一个Perl应用可以同一个同ASP会谈的PYTHON应用会谈,等等。系统集成商往往在不同系统之间建立自己的连接,创建它们自己定义的格式的协议来进行通信,但是这造成了大量的不常使用的协议。RPC方法的程序员无需了解底层的协议、网络以及各种实施细则.
XML-RPC 可以同 Python, Java, Perl, PHP, C, C++, Ruby, Microsoft’s .NET 以及许多其他的编程语言来一起使用。它的实现被广泛用于 Unix, Linux, Windows 和 Macintosh 的平台.
一个 XML-RPC 调用实在双方之间进行的,客户端(调用程序)和服务器(被调用过程)。服务时提供在一个特定的URL上的,比如 (such as http://example.org:8080/rpcserv/).
上面我们只是接触了 XML-RPC 的表面. 我推荐 O’Reilly’s “Programming Web Service with XML-RPC” 进行进一步的学习。还可以阅读以下几个环节:
OpenERP 基于C/S体系结构。服务器和客户端之间的通信使用XML-RPC协议。XML-RPC是一个非常简单的协议,它允许客户端进行远程过程调用。被调用的函数,它的参数,调用结果通过XML编码并且使用HTTP进行传输。欲了解更多的关于XML-RPC的详尽信息,请参阅: http://www.xml-rpc.com.
下面的图标综合了OpenERP的客户端和服务器结构。OpenERP的服务器和客户端通信使用 XML-RPC.
客户端
OpenERP 的逻辑是在服务器端配置的。客户端是很简单的,它是仅用于POST的数据(forms, lists, trees)并且把结果发回服务器。新功能的更新和加入并不需要客户端的升级,这使得OpenERP更容易维护.
客户端并不明白POST的内容。即使像点击打印图标的行动时发送到服务器并且询问如何作出反应.
客户端的操作时很简单的,当客户发出一个动作(保存一个表格、打开一个目录、打印…)它发送动作到服务器。然后服务器执行客户端的请求并将结果发送回来.
下面是三种行为;
- Open a window (form or tree)
- Print a document
- Execute a wizard
- 创建一个合作伙伴和他的地址
import xmlrpclib username = 'admin' # OpenERP 登陆用户 pwd = 'admin' # 登陆密码 dbname = 'terp' # OpenERP 帐套 # Get the uid sock_common = xmlrpclib.ServerProxy ('http://localhost:8069/xmlrpc/common') uid = sock_common.login(dbname, username, pwd) #replace localhost with the address of the server sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object') partner = { 'name': 'Fabien Pinckaers', 'lang': 'fr_FR', } partner_id = sock.execute(dbname, uid, pwd, 'res.partner', 'create', partner) address = { 'partner_id': partner_id, 'type' : 'default', 'street': 'Chaussée de Namur 40', 'zip': '1367', 'city': 'Grand-Rosière', 'phone': '+3281813700', 'fax': '+3281733501', } address_id = sock.execute(dbname, uid, pwd, 'res.partner.address', 'create', address)
查询业务伙伴
args = [('vat', '=', 'ZZZZZZ')] # 查询过滤条件 ids = sock.execute(dbname, uid, pwd, 'res.partner', 'search', args)
读取业务伙伴数据
fields = ['name', 'active', 'vat', 'ref'] # 需要读取的数据字段 data = sock.execute(dbname, uid, pwd, 'res.partner', 'read', ids, fields) #ids is a list of id
更新业务伙伴数据
values = {'vat': 'ZZ1ZZZ'} # 待更新数据Dictionary result = sock.execute(dbname, uid, pwd, 'res.partner', 'write', ids, values)
删除业务伙伴
# ids : 待删除业务伙伴id列表 result = sock.execute(dbname, uid, pwd, 'res.partner', 'unlink', ids)
下载 XML-RPC PHP Library
windows / linux: 从 http://phpxmlrpc.sourceforge.net/ 上下载xmlrpc框架,目前最新的正式版本是2007年2月25日发行的2.2版本
配置 PHP XML-RPC Library
从xmlrpc-2.2.tar.gz 解压出xmlrpc.inc 文件,把这个文件放到php函数库文件夹中,重启apache/iis 服务器
代码示例
function connect() { var $user = 'admin'; var $password = 'admin'; var $dbname = 'db_name'; var $server_url = 'http://localhost:8069/xmlrpc/'; if(isset($_COOKIE["user_id"]) == true) { if($_COOKIE["user_id"]>0) { return $_COOKIE["user_id"]; } } $sock = new xmlrpc_client($server_url.'common'); $msg = new xmlrpcmsg('login'); $msg->addParam(new xmlrpcval($dbname, "string")); $msg->addParam(new xmlrpcval($user, "string")); $msg->addParam(new xmlrpcval($password, "string")); $resp = $sock->send($msg); $val = $resp->value(); $id = $val->scalarval(); setcookie("user_id",$id,time()+3600); if($id > 0) { return $id; }else{ return -1; } }
/** * $client = xml-rpc handler * $relation = name of the relation ex: res.partner * $attribute = name of the attribute ex:code * $operator = search term operator ex: ilike, =, != * $key=search for */ function search($client,$relation,$attribute,$operator,$keys) { var $user = 'admin'; var $password = 'admin'; var $userId = -1; var $dbname = 'db_name'; var $server_url = 'http://localhost:8069/xmlrpc/'; $key = array(new xmlrpcval(array(new xmlrpcval($attribute , "string"), new xmlrpcval($operator,"string"), new xmlrpcval($keys,"string")),"array"), ); if($userId<=0) { connect(); } $msg = new xmlrpcmsg('execute'); $msg->addParam(new xmlrpcval($dbname, "string")); $msg->addParam(new xmlrpcval($userId, "int")); $msg->addParam(new xmlrpcval($password, "string")); $msg->addParam(new xmlrpcval($relation, "string")); $msg->addParam(new xmlrpcval("search", "string")); $msg->addParam(new xmlrpcval($key, "array")); $resp = $client->send($msg); $val = $resp->value(); $ids = $val->scalarval(); return $ids; }
<? include('xmlrpc.inc'); $arrayVal = array( 'name'=>new xmlrpcval('Fabien Pinckaers', "string") , 'vat'=>new xmlrpcval('BE477472701' , "string") ); $client = new xmlrpc_client("http://localhost:8069/xmlrpc/object"); $msg = new xmlrpcmsg('execute'); $msg->addParam(new xmlrpcval("dbname", "string")); $msg->addParam(new xmlrpcval("3", "int")); $msg->addParam(new xmlrpcval("demo", "string")); $msg->addParam(new xmlrpcval("res.partner", "string")); $msg->addParam(new xmlrpcval("create", "string")); $msg->addParam(new xmlrpcval($arrayVal, "struct")); $resp = $client->send($msg); if ($resp->faultCode()) echo 'Error: '.$resp->faultString(); else echo 'Partner '.$resp->value()->scalarval().' created !'; ?>
/** * $client = xml-rpc handler * $relation = name of the relation ex: res.partner * $attribute = name of the attribute ex:code * $operator = search term operator ex: ilike, =, != * $id = id of the record to be updated * $data = data to be updated */ function write($client,$relation,$attribute,$operator,$data,$id) { var $user = 'admin'; var $password = 'admin'; var $userId = -1; var $dbname = 'db_name'; var $server_url = 'http://localhost:8069/xmlrpc/'; $id_val = array(); $id_val[0] = new xmlrpcval($id, "int"); if($userId<=0) { connect(); } $msg = new xmlrpcmsg('execute'); $msg->addParam(new xmlrpcval($dbname, "string")); $msg->addParam(new xmlrpcval($userId, "int")); $msg->addParam(new xmlrpcval($password, "string")); $msg->addParam(new xmlrpcval($relation, "string")); $msg->addParam(new xmlrpcval("write", "string")); $msg->addParam(new xmlrpcval($id, "array")); $msg->addParam(new xmlrpcval($data, "struct")); $resp = $client->send($msg); $val = $resp->value(); $record = $val->scalarval(); return $record; }
下载 JAVA XML-RPC Library
从 http://ws.apache.org/xmlrpc/ 上下载java xmlrpc框架,目前最新版本是2007年8月发布的3.1版本. All OpenERP errors throw exceptions because the framework allows only an int as the error code where OpenERP returns a string.
代码示例
import java.net.URL; import java.util.Vector; import org.apache.commons.lang.StringUtils; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; public Vector<String> getDatabaseList(String host, int port) { XmlRpcClient xmlrpcDb = new XmlRpcClient(); XmlRpcClientConfigImpl xmlrpcConfigDb = new XmlRpcClientConfigImpl(); xmlrpcConfigDb.setEnabledForExtensions(true); xmlrpcConfigDb.setServerURL(new URL("http",host,port,"/xmlrpc/db")); xmlrpcDb.setConfig(xmlrpcConfigDb); try { //Retrieve databases Vector<Object> params = new Vector<Object>(); Object result = xmlrpcDb.execute("list", params); Object[] a = (Object[]) result; Vector<String> res = new Vector<String>(); for (int i = 0; i < a.length; i++) { if (a[i] instanceof String) { res.addElement((String)a[i]); } } catch (XmlRpcException e) { logger.warn("XmlException Error while retrieving OpenERP Databases: ",e); return -2; } catch (Exception e) { logger.warn("Error while retrieving OpenERP Databases: ",e); return -3; } }
import java.net.URL; import org.apache.commons.lang.StringUtils; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; public int Connect(String host, int port, String tinydb, String login, String password) { XmlRpcClient xmlrpcLogin = new XmlRpcClient(); XmlRpcClientConfigImpl xmlrpcConfigLogin = new XmlRpcClientConfigImpl(); xmlrpcConfigLogin.setEnabledForExtensions(true); xmlrpcConfigLogin.setServerURL(new URL("http",host,port,"/xmlrpc/common")); xmlrpcLogin.setConfig(xmlrpcConfigLogin); try { //Connect params = new Object[] {tinydb,login,password}; Object id = xmlrpcLogin.execute("login", params); if (id instanceof Integer) return (Integer)id; return -1; } catch (XmlRpcException e) { logger.warn("XmlException Error while logging to OpenERP: ",e); return -2; } catch (Exception e) { logger.warn("Error while logging to OpenERP: ",e); return -3; } }
查询业务伙伴
TODO
创建业务伙伴
TODO
更新业务伙伴
TODO
创建合作伙伴和他们的地址的例子.
import xmlrpclib sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object') uid = 1 pwd = 'demo' partner = { 'title': 'Monsieur', 'name': 'Fabien Pinckaers', 'lang': 'fr', 'active': True, } partner_id = sock.execute(dbname, uid, pwd, 'res.partner', 'create', partner) address = { 'partner_id': partner_id, 'type': 'default', 'street': 'Rue du vieux chateau, 21', 'zip': '1457', 'city': 'Walhain', 'phone': '(+32)10.68.94.39', 'fax': '(+32)10.68.94.39', } sock.execute(dbname, uid, pwd, 'res.partner.address', 'create', address)
用下面的脚本来获得用户的 UID :
sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/common') UID = sock.login('terp3', 'admin', 'admin')
CRUD(创建/读取/更新/删除)代码示例:
""" :The login function is under :: http://localhost:8069/xmlrpc/common :For object retrieval use: :: http://localhost:8069/xmlrpc/object """ import xmlrpclib user = 'admin' pwd = 'admin' dbname = 'terp3' model = 'res.partner' sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/common') uid = sock.login(dbname ,user ,pwd) sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object') # CREATE A PARTNER partner_data = {'name'.. code-block:: php:'Tiny', 'active':True, 'vat':'ZZZZZ'} partner_id = sock.execute(dbname, uid, pwd, model, 'create', partner_data) # The relation between res.partner and res.partner.category is of type many2many # To add categories to a partner use the following format: partner_data = {'name':'Provider2', 'category_id': [(6,0,[3, 2, 1])]} # Where [3, 2, 1] are id fields of lines in res.partner.category # SEARCH PARTNERS args = [('vat', '=', 'ZZZZZ'),] ids = sock.execute(dbname, uid, pwd, model, 'search', args) # READ PARTNER DATA fields = ['name', 'active', 'vat', 'ref'] results = sock.execute(dbname, uid, pwd, model, 'read', ids, fields) print results # EDIT PARTNER DATA values = {'vat':'ZZ1ZZ'} results = sock.execute(dbname, uid, pwd, model, 'write', ids, values) # DELETE PARTNER DATA results = sock.execute(dbname, uid, pwd, model, 'unlink', ids)
PRINT(打印) 示例代码:
- 打印发票
- IDS is the invoice ID, as returned by:
- ids = sock.execute(dbname, uid, pwd, ‘account.invoice’, ‘search’, [(‘number’, ‘ilike’, invoicenumber), (‘type’, ‘=’, ‘out_invoice’)])
import time import base64 printsock = xmlrpclib.ServerProxy('http://server:8069/xmlrpc/report') model = 'account.invoice' id_report = printsock.report(dbname, uid, pwd, model, ids, {'model': model, 'id': ids[0], 'report_type':'pdf'}) time.sleep(5) state = False attempt = 0 while not state: report = printsock.report_get(dbname, uid, pwd, id_report) state = report['state'] if not state: time.sleep(1) attempt += 1 if attempt>200: print 'Printing aborted, too long delay !' string_pdf = base64.decodestring(report['result']) file_pdf = open('/tmp/file.pdf','w') file_pdf.write(string_pdf) file_pdf.close()
下面的例子是如何使用 PHP 创建一个合作伙伴. 这里使用 phpxmlrpc 库, 在 sourceforge 上有效.
<? include('xmlrpc.inc'); $arrayVal = array( 'name'=>new xmlrpcval('Fabien Pinckaers', "string") , 'vat'=>new xmlrpcval('BE477472701' , "string") ); $client = new xmlrpc_client("http://localhost:8069/xmlrpc/object"); $msg = new xmlrpcmsg('execute'); $msg->addParam(new xmlrpcval("dbname", "string")); $msg->addParam(new xmlrpcval("3", "int")); $msg->addParam(new xmlrpcval("demo", "string")); $msg->addParam(new xmlrpcval("res.partner", "string")); $msg->addParam(new xmlrpcval("create", "string")); $msg->addParam(new xmlrpcval($arrayVal, "struct")); $resp = $client->send($msg); if ($resp->faultCode()) echo 'Error: '.$resp->faultString(); else echo 'Partner '.$resp->value()->scalarval().' created !'; ?>
下面的例子是使用 Perl 创建、查找、删除一个合作伙伴.
#!c:/perl/bin/perl # 17-02-2010 # OpenERP XML RPC communication example # Todor Todorov <[email protected]> <[email protected]> use strict; use Frontier::Client; use Data::Dumper; my($user) = 'admin'; my($pw) = 'admin'; my($db) = 'put_your_dbname_here'; my($model) = 'res.partner'; #登录 my $server_url = 'http://localhost:8069/xmlrpc/common'; my $server = Frontier::Client->new('url' => $server_url); my $uid = $server->call('login',$db,$user,$pw); print Dumper($uid); my $server_url = 'http://localhost:8069/xmlrpc/object'; my $server = Frontier::Client->new('url' => $server_url); print Dumper($server); # # 创建合作伙伴 # my $partner_data = {'name'=>'MyNewPartnerName', 'active'=> 'True', 'vat'=>'ZZZZZ'}; my $partner_id = $server->call('execute',$db, $uid, $pw, $model, 'create', $partner_data); print Dumper($partner_id); # # 搜索合作伙伴 # my $query = [['vat', '=', 'ZZZZZ']]; print Dumper($query); my $ids = $server->call('execute',$db, $uid, $pw, $model, 'search', $query); print Dumper($ids); #这里等待用户输入 #OpenERP interface my be checked if partner is shown there print $/."Check OpenERP if partner is inserted. Press ENTER".$/; <STDIN>; # # 删除合作伙伴 # my $results = $server->call('execute',$db, $uid, $pw, $model, 'unlink', $ids); print Dumper($results);
在 OpenERP 的 GTK 或 web 客户端的一切活动都是通过 XML/RPC webservices. 启动 openERP GTK 客户端 使用 ./openerp-client.py -l debug_rpc (or debug_rpc_answer) 然后你可以在 GTK 客户端操作,查看客户端操作日志, 你将看到webservice的标签。通过在日志中创建缩进将帮助您 找出它的web服务.