Odoo is usually extended internally via modules, but many of its features and
all of its data are also available from the outside for external analysis or
integration with various tools. Part of the Model Reference API is
easily available over XML-RPC and accessible from a variety of languages.Connection
import xmlrpclib
info = xmlrpclib.ServerProxy('https://demo.odoo.com/start').start()
url, db, username, password = \
info['host'], info['database'], info['user'], info['password']
common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
uid = common.authenticate(db, username, password, {})
models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
require "xmlrpc/client"
info = XMLRPC::Client.new2('https://demo.odoo.com/start').call('start')
url, db, username, password = \
info['host'], info['database'], info['user'], info['password']
common = XMLRPC::Client.new2("#{url}/xmlrpc/2/common")
uid = common.call('authenticate', db, username, password, {})
models = XMLRPC::Client.new2("#{url}/xmlrpc/2/object").proxy
require_once('ripcord.php');
$info = ripcord::client('https://demo.odoo.com/start')->start();
list($url, $db, $username, $password) =
array($info['host'], $info['database'], $info['user'], $info['password']);
$common = ripcord::client("$url/xmlrpc/2/common");
$uid = $common->authenticate($db, $username, $password, array());
$models = ripcord::client("$url/xmlrpc/2/object");
final XmlRpcClient client = new XmlRpcClient();
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
final Map info = (Map)client.execute(
start_config, "start", emptyList());
final String url = info.get("host"),
db = info.get("database"),
username = info.get("user"),
password = info.get("password");
final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
common_config.setServerURL(new URL(String.format("%s/xmlrpc/2/common", url)));
int uid = (int)client.execute(
common_config, "authenticate", Arrays.asList(
db, username, password, emptyMap()));
final XmlRpcClient models = new XmlRpcClient() {{
setConfig(new XmlRpcClientConfigImpl() {{
setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
}});
}};Configuration
If you already have an Odoo server installed, you can just use its
parameters
Warning
For Odoo Online instances (.odoo.com), users are created without a
local password (as a person you are logged in via the Odoo Online
authentication system, not by the instance itself). To use XML-RPC on Odoo
Online instances, you will need to set a password on the user account you
want to use:Log in your instance with an administrator account
Go to Settings ‣ Users ‣ Users
Click on the user you want to use for XML-RPC access
Click the Change Password button
Set a New Password value then click
Change Password.
The server url is the instance's domain (e.g.
https://mycompany.odoo.com), the database name is the name of the
instance (e.g. mycompany). The username is the configured user's login
as shown by the Change Password screen.
Python
Ruby
PHP
Java
url =
db =
username = 'admin'
password =
url =
db =
username = "admin"
password =
$url = ;
$db = ;
$username = "admin";
$password = ;
final String url = ,
db = ,
username = "admin",
password = ;demo
To make exploration simpler, you can also ask https://demo.odoo.com for a test
database:
Python
Ruby
PHP
Java
import xmlrpclib
info = xmlrpclib.ServerProxy('https://demo.odoo.com/start').start()
url, db, username, password = \
info['host'], info['database'], info['user'], info['password']
require "xmlrpc/client"
info = XMLRPC::Client.new2('https://demo.odoo.com/start').call('start')
url, db, username, password = \
info['host'], info['database'], info['user'], info['password']
require_once('ripcord.php');
$info = ripcord::client('https://demo.odoo.com/start')->start();
list($url, $db, $username, $password) =
array($info['host'], $info['database'], $info['user'], $info['password']);
Note
These examples use the Ripcord
library, which provides a simple XML-RPC API. Ripcord requires that
XML-RPC support be enabled in your PHP
installation.
Since calls are performed over
HTTPS, it also requires that
the OpenSSL extension be enabled.
final XmlRpcClient client = new XmlRpcClient();
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
final Map info = (Map)client.execute(
start_config, "start", emptyList());
final String url = info.get("host"),
db = info.get("database"),
username = info.get("user"),
password = info.get("password");
Note
These examples use the Apache XML-RPC library
The examples do not include imports as these imports couldn't be
pasted in the code.Logging in
Odoo requires users of the API to be authenticated before they can query most
data.
The xmlrpc/2/common endpoint provides meta-calls which don't require
authentication, such as the authentication itself or fetching version
information. To verify if the connection information is correct before trying
to authenticate, the simplest call is to ask for the server's version. The
authentication itself is done through the authenticate function and
returns a user identifier (uid) used in authenticated calls instead of
the login.
Python
Ruby
PHP
Java
common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
common.version()
common = XMLRPC::Client.new2("#{url}/xmlrpc/2/common")
common.call('version')
$common = ripcord::client("$url/xmlrpc/2/common");
$common->version();
final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
common_config.setServerURL(
new URL(String.format("%s/xmlrpc/2/common", url)));
client.execute(common_config, "version", emptyList());
{
"server_version": "8.0",
"server_version_info": [8, 0, 0, "final", 0],
"server_serie": "8.0",
"protocol_version": 1,
}
Python
Ruby
PHP
Java
uid = common.authenticate(db, username, password, {})
uid = common.call('authenticate', db, username, password, {})
$uid = $common->authenticate($db, $username, $password, array());
int uid = (int)client.execute(
common_config, "authenticate", asList(
db, username, password, emptyMap()));Calling methods
The second endpoint is xmlrpc/2/object, is used to call methods of odoo
models via the execute_kw RPC function.
Each call to execute_kw takes the following parameters:the database to use, a string
the user id (retrieved through authenticate), an integer
the user's password, a string
the model name, a string
the method name, a string
an array/list of parameters passed by position
a mapping/dict of parameters to pass by keyword (optional)
For instance to see if we can read the res.partner model we can call
check_access_rights with operation passed by position and
raise_exception passed by keyword (in order to get a true/false result
rather than true/error):
Python
Ruby
PHP
Java
models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
models.execute_kw(db, uid, password,
'res.partner', 'check_access_rights',
['read'], {'raise_exception': False})
models = XMLRPC::Client.new2("#{url}/xmlrpc/2/object").proxy
models.execute_kw(db, uid, password,
'res.partner', 'check_access_rights',
['read'], {raise_exception: false})
$models = ripcord::client("$url/xmlrpc/2/object");
$models->execute_kw($db, $uid, $password,
'res.partner', 'check_access_rights',
array('read'), array('raise_exception' => false));
final XmlRpcClient models = new XmlRpcClient() {{
setConfig(new XmlRpcClientConfigImpl() {{
setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
}});
}};
models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "check_access_rights",
asList("read"),
new HashMap() {{ put("raise_exception", false); }}
));
trueList records
Records can be listed and filtered via search().
search() takes a mandatory
domain filter (possibly empty), and returns the
database identifiers of all records matching the filter. To list customer
companies for instance:
Python
Ruby
PHP
Java
models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', True], ['customer', '=', True]]])
models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', true], ['customer', '=', true]]])
$models->execute_kw($db, $uid, $password,
'res.partner', 'search', array(
array(array('is_company', '=', true),
array('customer', '=', true))));
asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search",
asList(asList(
asList("is_company", "=", true),
asList("customer", "=", true)))
)));
[7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74]Pagination
By default a search will return the ids of all records matching the
condition, which may be a huge number. offset and limit parameters are
available to only retrieve a subset of all matched records.
Python
Ruby
PHP
Java
models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', True], ['customer', '=', True]]],
{'offset': 10, 'limit': 5})
models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', true], ['customer', '=', true]]],
{offset: 10, limit: 5})
$models->execute_kw($db, $uid, $password,
'res.partner', 'search',
array(array(array('is_company', '=', true),
array('customer', '=', true))),
array('offset'=>10, 'limit'=>5));
asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search",
asList(asList(
asList("is_company", "=", true),
asList("customer", "=", true))),
new HashMap() {{ put("offset", 10); put("limit", 5); }}
)));
[13, 20, 30, 22, 29]Count records
Rather than retrieve a possibly gigantic list of records and count them,
search_count() can be used to retrieve
only the number of records matching the query. It takes the same
domain filter as
search() and no other parameter.
Python
Ruby
PHP
Java
models.execute_kw(db, uid, password,
'res.partner', 'search_count',
[[['is_company', '=', True], ['customer', '=', True]]])
models.execute_kw(db, uid, password,
'res.partner', 'search_count',
[[['is_company', '=', true], ['customer', '=', true]]])
$models->execute_kw($db, $uid, $password,
'res.partner', 'search_count',
array(array(array('is_company', '=', true),
array('customer', '=', true))));
(Integer)models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search_count",
asList(asList(
asList("is_company", "=", true),
asList("customer", "=", true)))
));
19
Warning
calling search then search_count (or the other way around) may not
yield coherent results if other users are using the server: stored data
could have changed between the callsRead records
Record data is accessible via the read() method,
which takes a list of ids (as returned by
search()) and optionally a list of fields to
fetch. By default, it will fetch all the fields the current user can read,
which tends to be a huge amount.
Python
Ruby
PHP
Java
ids = models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', True], ['customer', '=', True]]],
{'limit': 1})
[record] = models.execute_kw(db, uid, password,
'res.partner', 'read', [ids])
# count the number of fields fetched by default
len(record)
ids = models.execute_kw(db, uid, password,
'res.partner', 'search',
[[['is_company', '=', true], ['customer', '=', true]]],
{limit: 1})
record = models.execute_kw(db, uid, password,
'res.partner', 'read', [ids]).first
# count the number of fields fetched by default
record.length
$ids = $models->execute_kw($db, $uid, $password,
'res.partner', 'search',
array(array(array('is_company', '=', true),
array('customer', '=', true))),
array('limit'=>1));
$records = $models->execute_kw($db, $uid, $password,
'res.partner', 'read', array($ids));
// count the number of fields fetched by default
count($records[0]);
final List ids = asList((Object[])models.execute(
"execute_kw", asList(
db, uid, password,
"res.partner", "search",
asList(asList(
asList("is_company", "=", true),
asList("customer", "=", true))),
new HashMap() {{ put("limit", 1); }})));
final Map record = (Map)((Object[])models.execute(
"execute_kw", asList(
db, uid, password,
"res.partner", "read",
asList(ids)
)
))[0];
// count the number of fields fetched by default
record.size();
121
Conversedly, picking only three fields deemed interesting.
Python
Ruby
PHP
Java
models.execute_kw(db, uid, password,
'res.partner', 'read',
[ids], {'fields': ['name', 'country_id', 'comment']})
models.execute_kw(db, uid, password,
'res.partner', 'read',
[ids], {fields: %w(name country_id comment)})
$models->execute_kw($db, $uid, $password,
'res.partner', 'read',
array($ids),
array('fields'=>array('name', 'country_id', 'comment')));
asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "read",
asList(ids),
new HashMap() {{
put("fields", asList("name", "country_id", "comment"));
}}
)));
[{"comment": false, "country_id": [21, "Belgium"], "id": 7, "name": "Agrolait"}]
Note
even if the id field is not requested, it is always returnedListing record fields
fields_get() can be used to inspect
a model's fields and check which ones seem to be of interest.
Because it returns a large amount of meta-information (it is also used by client
programs) it should be filtered before printing, the most interesting items
for a human user are string (the field's label), help (a help text if
available) and type (to know which values to expect, or to send when
updating a record):
Python
Ruby
PHP
Java
models.execute_kw(
db, uid, password, 'res.partner', 'fields_get',
[], {'attributes': ['string', 'help', 'type']})
models.execute_kw(
db, uid, password, 'res.partner', 'fields_get',
[], {attributes: %w(string help type)})
$models->execute_kw($db, $uid, $password,
'res.partner', 'fields_get',
array(), array('attributes' => array('string', 'help', 'type')));
(Map>)models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "fields_get",
emptyList(),
new HashMap() {{
put("attributes", asList("string", "help", "type"));
}}
));
{
"ean13": {
"type": "char",
"help": "BarCode",
"string": "EAN13"
},
"property_account_position": {
"type": "many2one",
"help": "The fiscal position will determine taxes and accounts used for the partner.",
"string": "Fiscal Position"
},
"signup_valid": {
"type": "boolean",
"help": "",
"string": "Signup Token is Valid"
},
"date_localization": {
"type": "date",
"help": "",
"string": "Geo Localization Date"
},
"ref_companies": {
"type": "one2many",
"help": "",
"string": "Companies that refers to partner"
},
"sale_order_count": {
"type": "integer",
"help": "",
"string": "# of Sales Order"
},
"purchase_order_count": {
"type": "integer",
"help": "",
"string": "# of Purchase Order"
},Search and read
Because it is a very common task, Odoo provides a
search_read() shortcut which as its name notes is
equivalent to a search() followed by a
read(), but avoids having to perform two requests
and keep ids around.
Its arguments are similar to search()'s, but it
can also take a list of fields (like read(),
if that list is not provided it will fetch all fields of matched records):
Python
Ruby
PHP
Java
models.execute_kw(db, uid, password,
'res.partner', 'search_read',
[[['is_company', '=', True], ['customer', '=', True]]],
{'fields': ['name', 'country_id', 'comment'], 'limit': 5})
models.execute_kw(db, uid, password,
'res.partner', 'search_read',
[[['is_company', '=', true], ['customer', '=', true]]],
{fields: %w(name country_id comment), limit: 5})
$models->execute_kw($db, $uid, $password,
'res.partner', 'search_read',
array(array(array('is_company', '=', true),
array('customer', '=', true))),
array('fields'=>array('name', 'country_id', 'comment'), 'limit'=>5));
asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search_read",
asList(asList(
asList("is_company", "=", true),
asList("customer", "=", true))),
new HashMap() {{
put("fields", asList("name", "country_id", "comment"));
put("limit", 5);
}}
)));
[
{
"comment": false,
"country_id": [ 21, "Belgium" ],
"id": 7,
"name": "Agrolait"
},
{
"comment": false,
"country_id": [ 76, "France" ],
"id": 18,
"name": "Axelor"
},
{
"comment": false,
"country_id": [ 233, "United Kingdom" ],
"id": 12,
"name": "Bank Wealthy and sons"
},
{
"comment": false,
"country_id": [ 105, "India" ],
"id": 14,
"name": "Best Designers"
},
{
"comment": false,
"country_id": [ 76, "France" ],
"id": 17,
"name": "Camptocamp"
}
]Create records
Records of a model are created using create(). The
method will create a single record and return its database identifier.
create() takes a mapping of fields to values, used
to initialize the record. For any field which has a default value and is not
set through the mapping argument, the default value will be used.
Python
Ruby
PHP
Java
id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{
'name': "New Partner",
}])
id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{
name: "New Partner",
}])
$id = $models->execute_kw($db, $uid, $password,
'res.partner', 'create',
array(array('name'=>"New Partner")));
final Integer id = (Integer)models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "create",
asList(new HashMap() {{ put("name", "New Partner"); }})
));
78
Warning
while most value types are what would be expected (integer for
Integer, string for Char
or Text),Date, Datetime and
Binary fields use string valuesUpdate records
Records can be updated using write(), it takes
a list of records to update and a mapping of updated fields to values similar
to create().
Multiple records can be updated simultanously, but they will all get the same
values for the fields being set. It is not currently possible to perform
"computed" updates (where the value being set depends on an existing value of
a record).
Python
Ruby
PHP
Java
models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {
'name': "Newer partner"
}])
# get record name after having changed it
models.execute_kw(db, uid, password, 'res.partner', 'name_get', [[id]])
models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {
name: "Newer partner"
}])
# get record name after having changed it
models.execute_kw(db, uid, password, 'res.partner', 'name_get', [[id]])
$models->execute_kw($db, $uid, $password, 'res.partner', 'write',
array(array($id), array('name'=>"Newer partner")));
// get record name after having changed it
$models->execute_kw($db, $uid, $password,
'res.partner', 'name_get', array(array($id)));
models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "write",
asList(
asList(id),
new HashMap() {{ put("name", "Newer Partner"); }}
)
));
// get record name after having changed it
asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "name_get",
asList(asList(id))
)));
[[78, "Newer partner"]]Delete records
Records can be deleted in bulk by providing their ids to
unlink().
Python
Ruby
PHP
Java
models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
# check if the deleted record is still in the database
models.execute_kw(db, uid, password,
'res.partner', 'search', [[['id', '=', id]]])
models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
# check if the deleted record is still in the database
models.execute_kw(db, uid, password,
'res.partner', 'search', [[['id', '=', id]]])
$models->execute_kw($db, $uid, $password,
'res.partner', 'unlink',
array(array($id)));
// check if the deleted record is still in the database
$models->execute_kw($db, $uid, $password,
'res.partner', 'search',
array(array(array('id', '=', $id))));
models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "unlink",
asList(asList(id))));
// check if the deleted record is still in the database
asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search",
asList(asList(asList("id", "=", 78)))
)));
[]Inspection and introspection
While we previously used fields_get() to query a
model and have been using an arbitrary model from the start, Odoo stores
most model metadata inside a few meta-models which allow both querying the
system and altering models and fields (with some limitations) on the fly over
XML-RPC.ir.model
Provides information about Odoo models via its various fieldsnamea human-readable description of the modelmodelthe name of each model in the systemstatewhether the model was generated in Python code (base) or by creating
an ir.model record (manual)field_idlist of the model's fields through a One2many to
ir.model.fieldsview_idsOne2many to the Views defined
for the modelaccess_idsOne2many relation to the
Access Control set on the model
ir.model can be used toquery the system for installed models (as a precondition to operations
on the model or to explore the system's content)
get information about a specific model (generally by listing the fields
associated with it)
create new models dynamically over RPC
Warning"custom" model names must start with x_
the state must be provided and manual, otherwise the model will
not be loaded
it is not possible to add new methods to a custom model, only fields
a custom model will initially contain only the "built-in" fields available
on all models:
Python
PHP
Ruby
Java
models.execute_kw(db, uid, password, 'ir.model', 'create', [{
'name': "Custom Model",
'model': "x_custom_model",
'state': 'manual',
}])
models.execute_kw(
db, uid, password, 'x_custom_model', 'fields_get',
[], {'attributes': ['string', 'help', 'type']})
$models->execute_kw(
$db, $uid, $password,
'ir.model', 'create', array(array(
'name' => "Custom Model",
'model' => 'x_custom_model',
'state' => 'manual'
))
);
$models->execute_kw(
$db, $uid, $password,
'x_custom_model', 'fields_get',
array(),
array('attributes' => array('string', 'help', 'type'))
);
models.execute_kw(
db, uid, password,
'ir.model', 'create', [{
name: "Custom Model",
model: 'x_custom_model',
state: 'manual'
}])
fields = models.execute_kw(
db, uid, password, 'x_custom_model', 'fields_get',
[], {attributes: %w(string help type)})
models.execute(
"execute_kw", asList(
db, uid, password,
"ir.model", "create",
asList(new HashMap() {{
put("name", "Custom Model");
put("model", "x_custom_model");
put("state", "manual");
}})
));
final Object fields = models.execute(
"execute_kw", asList(
db, uid, password,
"x_custom_model", "fields_get",
emptyList(),
new HashMap () {{
put("attributes", asList(
"string",
"help",
"type"));
}}
));
{
"create_uid": {
"type": "many2one",
"string": "Created by"
},
"create_date": {
"type": "datetime",
"string": "Created on"
},
"__last_update": {
"type": "datetime",
"string": "Last Modified on"
},
"write_uid": {
"type": "many2one",
"string": "Last Updated by"
},
"write_date": {
"type": "datetime",
"string": "Last Updated on"
},
"display_name": {
"type": "char",
"string": "Display Name"
},
"id": {
"type": "integer",
"string": "Id"
}
}ir.model.fields
Provides information about the fields of Odoo models and allows adding
custom fields without using Python codemodel_idMany2one to
ir.model to which the field belongsnamethe field's technical name (used in read or write)field_descriptionthe field's user-readable label (e.g. string in fields_get)ttypethe type of field to createstatewhether the field was created via Python code (base) or via
ir.model.fields (manual)required, readonly, translateenables the corresponding flag on the fieldgroupsselection, size, on_delete, relation, relation_field, domaintype-specific properties and customizations, see the fields
documentation for details
Like custom models, only new fields created with state="manual" are
activated as actual fields on the model.
Warning
computed fields can not be added via ir.model.fields, some
field meta-information (defaults, onchange) can not be set either
Python
PHP
Ruby
Java
id = models.execute_kw(db, uid, password, 'ir.model', 'create', [{
'name': "Custom Model",
'model': "x_custom",
'state': 'manual',
}])
models.execute_kw(
db, uid, password,
'ir.model.fields', 'create', [{
'model_id': id,
'name': 'x_name',
'ttype': 'char',
'state': 'manual',
'required': True,
}])
record_id = models.execute_kw(
db, uid, password,
'x_custom', 'create', [{
'x_name': "test record",
}])
models.execute_kw(db, uid, password, 'x_custom', 'read', [[record_id]])
$id = $models->execute_kw(
$db, $uid, $password,
'ir.model', 'create', array(array(
'name' => "Custom Model",
'model' => 'x_custom',
'state' => 'manual'
))
);
$models->execute_kw(
$db, $uid, $password,
'ir.model.fields', 'create', array(array(
'model_id' => $id,
'name' => 'x_name',
'ttype' => 'char',
'state' => 'manual',
'required' => true
))
);
$record_id = $models->execute_kw(
$db, $uid, $password,
'x_custom', 'create', array(array(
'x_name' => "test record"
))
);
$models->execute_kw(
$db, $uid, $password,
'x_custom', 'read',
array(array($record_id)));
id = models.execute_kw(
db, uid, password,
'ir.model', 'create', [{
name: "Custom Model",
model: "x_custom",
state: 'manual'
}])
models.execute_kw(
db, uid, password,
'ir.model.fields', 'create', [{
model_id: id,
name: "x_name",
ttype: "char",
state: "manual",
required: true
}])
record_id = models.execute_kw(
db, uid, password,
'x_custom', 'create', [{
x_name: "test record"
}])
models.execute_kw(
db, uid, password,
'x_custom', 'read', [[record_id]])
final Integer id = (Integer)models.execute(
"execute_kw", asList(
db, uid, password,
"ir.model", "create",
asList(new HashMap() {{
put("name", "Custom Model");
put("model", "x_custom");
put("state", "manual");
}})
));
models.execute(
"execute_kw", asList(
db, uid, password,
"ir.model.fields", "create",
asList(new HashMap() {{
put("model_id", id);
put("name", "x_name");
put("ttype", "char");
put("state", "manual");
put("required", true);
}})
));
final Integer record_id = (Integer)models.execute(
"execute_kw", asList(
db, uid, password,
"x_custom", "create",
asList(new HashMap() {{
put("x_name", "test record");
}})
));
client.execute(
"execute_kw", asList(
db, uid, password,
"x_custom", "read",
asList(asList(record_id))
));
[
{
"create_uid": [1, "Administrator"],
"x_name": "test record",
"__last_update": "2014-11-12 16:32:13",
"write_uid": [1, "Administrator"],
"write_date": "2014-11-12 16:32:13",
"create_date": "2014-11-12 16:32:13",
"id": 1,
"display_name": "test record"
}
]Workflow manipulations
Workflows can be moved along by sending them signals.
Instead of using the top-level execute_kw, signals are sent using
exec_workflow.
Signals are sent to a specific record, and possibly trigger a transition on
the workflow instance associated with the record.
Warning
this example needs account module installed
Python
PHP
Ruby
Java
client = models.execute_kw(
db, uid, password,
'res.partner', 'search_read',
[[('customer', '=', True)]],
{'limit': 1, 'fields': [
'property_account_receivable',
'property_payment_term',
'property_account_position']
})[0]
invoice_id = models.execute_kw(
db, uid, password,
'account.invoice', 'create', [{
'partner_id': client['id'],
'account_id': client['property_account_receivable'][0],
'invoice_line': [(0, False, {'name': "AAA"})]
}])
models.exec_workflow(
db, uid, password, 'account.invoice', 'invoice_open', invoice_id)
$client = $models->execute_kw(
$db, $uid, $password,
'res.partner', 'search_read',
array(array(array('customer', '=', true))),
array(
'limit' => 1,
'fields' => array(
'property_account_receivable',
'property_payment_term',
'property_account_position'
)))[0];
$invoice_id = $models->execute_kw(
$db, $uid, $password,
'account.invoice', 'create', array(array(
'partner_id' => $client['id'],
'account_id' => $client['property_account_receivable'][0],
'invoice_line' => array(array(0, false, array('name' => "AAA")))
)));
$models->exec_workflow(
$db, $uid, $password,
'account.invoice', 'invoice_open',
$invoice_id);
client = models.execute_kw(
db, uid, password,
'res.partner', 'search_read',
[[['customer', '=', true]]],
{limit: 1, fields: %w(property_account_receivable property_payment_term property_account_position)}
)[0]
invoice_id = models.execute_kw(
db, uid, password,
'account.invoice', 'create', [{
partner_id: client['id'],
account_id: client['property_account_receivable'][0],
invoice_line: [[0, false, {name: "AAA"}]]
}])
models.exec_workflow(
db, uid, password,
'account.invoice', 'invoice_open', invoice_id)
final Map c = (Map)
((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search_read",
asList(
asList(
asList("customer", "=", true))),
new HashMap() {{
put("limit", 1);
put("fields", asList(
"property_account_receivable",
"property_payment_term",
"property_account_position"
));
}}
)))[0];
final Integer invoice_id = (Integer)models.execute(
"execute_kw", asList(
db, uid, password,
"account.invoice", "create",
asList(new HashMap() {{
put("partner_id", c.get("id"));
put("account_id", ((Object[])c.get("property_account_receivable"))[0]);
put("invoice_line", asList(
asList(0, false, new HashMap() {{
put("name", "AAA");
}})
));
}})
));
models.execute(
"exec_workflow", asList(
db, uid, password,
"account.invoice", "invoice_open", invoice_id));Report printing
Available reports can be listed by searching the ir.actions.report.xml
model, fields of interest beingmodelthe model on which the report applies, can be used to look for available
reports on a specific modelnamehuman-readable report namereport_namethe technical name of the report, used to print it
Reports can be printed over RPC with the following information:the name of the report (report_name)
the ids of the records to include in the report
Python
PHP
Ruby
Java
invoice_ids = models.execute_kw(
db, uid, password, 'account.invoice', 'search',
[[('type', '=', 'out_invoice'), ('state', '=', 'open')]])
report = xmlrpclib.ServerProxy('{}/xmlrpc/2/report'.format(url))
result = report.render_report(
db, uid, password, 'account.report_invoice', invoice_ids)
report_data = result['result'].decode('base64')
$invoice_ids = $models->execute_kw(
$db, $uid, $password,
'account.invoice', 'search',
array(array(array('type', '=', 'out_invoice'),
array('state', '=', 'open'))));
$report = ripcord::client("$url/xmlrpc/2/report");
$result = $report->render_report(
$db, $uid, $password,
'account.report_invoice', $invoice_ids);
$report_data = base64_decode($result['result']);
require 'base64'
invoice_ids = models.execute_kw(
db, uid, password,
'account.invoice', 'search',
[[['type', '=', 'out_invoice'], ['state', '=', 'open']]])
report = XMLRPC::Client.new2("#{url}/xmlrpc/2/report").proxy
result = report.render_report(
db, uid, password,
'account.report_invoice', invoice_ids)
report_data = Base64.decode64(result['result'])
final Object[] invoice_ids = (Object[])models.execute(
"execute_kw", asList(
db, uid, password,
"account.invoice", "search",
asList(asList(
asList("type", "=", "out_invoice"),
asList("state", "=", "open")))
));
final XmlRpcClientConfigImpl report_config = new XmlRpcClientConfigImpl();
report_config.setServerURL(
new URL(String.format("%s/xmlrpc/2/report", url)));
final Map result = (Map)client.execute(
report_config, "render_report", asList(
db, uid, password,
"account.report_invoice",
invoice_ids));
final byte[] report_data = DatatypeConverter.parseBase64Binary(
(String)result.get("result"));
Note
the report is sent as PDF binary data encoded in base64, it must be
decoded and may need to be saved to disk before use