7 Odoo is mostly extended internally via modules, but much of its features and
8 all of its data is also available from the outside for external analysis or
9 integration with various tools. Part of the :ref:`reference/orm/model` API is
10 easily available over XML-RPC_ and accessible from a variety of languages.
12 .. Odoo XML-RPC idiosyncracies:
13 * uses multiple endpoint and a nested call syntax instead of a
14 "hierarchical" server structure (e.g. ``openerp.res.partner.read()``)
15 * uses its own own manual auth system instead of basic auth or sessions
16 (basic is directly supported the Python and Ruby stdlibs as well as
17 ws-xmlrpc, not sure about ripcord)
18 * own auth is inconvenient as (uid, password) have to be explicitly passed
19 into every call. Session would allow db to be stored as well
20 These issues are especially visible in Java, somewhat less so in PHP
25 .. kinda gross because it duplicates existing bits
29 .. rst-class:: setupcode hidden
31 .. code-block:: python
34 info = xmlrpclib.ServerProxy('https://demo.odoo.com/start').start()
35 url, db, username, password = \
36 info['host'], info['database'], info['user'], info['password']
37 common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
38 uid = common.authenticate(db, username, password, {})
39 models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
43 require "xmlrpc/client"
44 info = XMLRPC::Client.new2('https://demo.odoo.com/start').call('start')
45 url, db, username, password = \
46 info['host'], info['database'], info['user'], info['password']
47 common = XMLRPC::Client.new2("#{url}/xmlrpc/2/common")
48 uid = common.call('authenticate', db, username, password, {})
49 models = XMLRPC::Client.new2("#{url}/xmlrpc/2/object").proxy
53 require_once('ripcord.php');
54 $info = ripcord::client('https://demo.odoo.com/start')->start();
55 list($url, $db, $username, $password) =
56 array($info['host'], $info['database'], $info['user'], $info['password']);
57 $common = ripcord::client("$url/xmlrpc/2/common");
58 $uid = $common->authenticate($db, $username, $password, array());
59 $models = ripcord::client("$url/xmlrpc/2/object");
63 final XmlRpcClient client = new XmlRpcClient();
64 final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
65 start_config.setServerURL(new URL("https://demo.odoo.com/start"));
66 final Map<String, String> info = (Map<String, String>)client.execute(
67 start_config, "start", emptyList());
69 final String url = info.get("host"),
70 db = info.get("database"),
71 username = info.get("user"),
72 password = info.get("password");
74 final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
75 common_config.setServerURL(new URL(String.format("%s/xmlrpc/2/common", url)));
77 int uid = (int)client.execute(
78 common_config, "authenticate", Arrays.asList(
79 db, username, password, emptyMap()));
81 final XmlRpcClient models = new XmlRpcClient() {{
82 setConfig(new XmlRpcClientConfigImpl() {{
83 setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
90 If you already have an Odoo server installed, you can just use its
93 .. rst-class:: switchable setup
95 .. code-block:: python
97 url = <insert server URL>
98 db = <insert database name>
100 password = <insert password for your admin user (default: admin)>
104 url = <insert server URL>
105 db = <insert database name>
107 password = <insert password for your admin user (default: admin)>
111 $url = <insert server URL>;
112 $db = <insert database name>;
114 $password = <insert password for your admin user (default: admin)>;
118 final String url = <insert server URL>,
119 db = <insert database name>,
121 password = <insert password for your admin user (default: admin)>;
123 To make exploration simpler, you can also ask https://demo.odoo.com for a test
126 .. rst-class:: switchable setup
128 .. code-block:: python
131 info = xmlrpclib.ServerProxy('https://demo.odoo.com/start').start()
132 url, db, username, password = \
133 info['host'], info['database'], info['user'], info['password']
137 require "xmlrpc/client"
138 info = XMLRPC::Client.new2('https://demo.odoo.com/start').call('start')
139 url, db, username, password = \
140 info['host'], info['database'], info['user'], info['password']
144 require_once('ripcord.php');
145 $info = ripcord::client('https://demo.odoo.com/start')->start();
146 list($url, $db, $username, $password) =
147 array($info['host'], $info['database'], $info['user'], $info['password']);
151 final XmlRpcClient client = new XmlRpcClient();
153 final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
154 start_config.setServerURL(new URL("https://demo.odoo.com/start"));
155 final Map<String, String> info = (Map<String, String>)client.execute(
156 start_config, "start", emptyList());
158 final String url = info.get("host"),
159 db = info.get("database"),
160 username = info.get("user"),
161 password = info.get("password");
163 .. rst-class:: force-right
168 These examples use the `Ripcord <https://code.google.com/p/ripcord/>`_
169 library, which provides a simple XML-RPC API. Ripcord requires that
170 `XML-RPC support be enabled
171 <http://php.net/manual/en/xmlrpc.installation.php>`_ in your PHP
174 Since calls are performed over
175 `HTTPS <http://en.wikipedia.org/wiki/HTTP_Secure>`_, it also requires that
176 the `OpenSSL extension
177 <http://php.net/manual/en/openssl.installation.php>`_ be enabled.
182 These examples use the `Apache XML-RPC library
183 <https://ws.apache.org/xmlrpc/>`_
185 The examples do not include imports as these imports couldn't be
191 Odoo requires users of the API to be authenticated before being able to query
194 The ``xmlrpc/2/common`` endpoint provides meta-calls which don't require
195 authentication, such as the authentication itself or fetching version
196 information. To verify if the connection information is correct before trying
197 to authenticate, the simplest call is to ask for the server's version. The
198 authentication itself is done through the ``authenticate`` function and
199 returns a user identifier (``uid``) used in authenticated calls instead of
202 .. rst-class:: switchable setup
204 .. code-block:: python
206 common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
211 common = XMLRPC::Client.new2("#{url}/xmlrpc/2/common")
212 common.call('version')
216 $common = ripcord::client("$url/xmlrpc/2/common");
221 final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
222 common_config.setServerURL(
223 new URL(String.format("%s/xmlrpc/2/common", url)));
224 client.execute(common_config, "version", emptyList());
229 "server_version": "8.0",
230 "server_version_info": [8, 0, 0, "final", 0],
231 "server_serie": "8.0",
232 "protocol_version": 1,
235 .. rst-class:: switchable setup
237 .. code-block:: python
239 uid = common.authenticate(db, username, password, {})
243 uid = common.call('authenticate', db, username, password, {})
247 $uid = $common->authenticate($db, $username, $password, array());
251 int uid = (int)client.execute(
252 common_config, "authenticate", asList(
253 db, username, password, emptyMap()));
258 The second endpoint is ``xmlrpc/2/object``, is used to call methods of odoo
259 models via the ``execute_kw`` RPC function.
261 Each call to ``execute_kw`` takes the following parameters:
263 * the database to use, a string
264 * the user id (retrieved through ``authenticate``), an integer
265 * the user's password, a string
266 * the model name, a string
267 * the method name, a string
268 * an array/list of parameters passed by position
269 * a mapping/dict of parameters to pass by keyword (optional)
271 .. rst-class:: force-right
273 For instance to see if we can read the ``res.partner`` model we can call
274 ``check_access_rights`` with ``operation`` passed by position and
275 ``raise_exception`` passed by keyword (in order to get a true/false result
276 rather than true/error):
278 .. rst-class:: switchable setup
280 .. code-block:: python
282 models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
283 models.execute_kw(db, uid, password,
284 'res.partner', 'check_access_rights',
285 ['read'], {'raise_exception': False})
289 models = XMLRPC::Client.new2("#{url}/xmlrpc/2/object").proxy
290 models.execute_kw(db, uid, password,
291 'res.partner', 'check_access_rights',
292 ['read'], {raise_exception: false})
296 $models = ripcord::client("$url/xmlrpc/2/object");
297 $models->execute_kw($db, $uid, $password,
298 'res.partner', 'check_access_rights',
299 array('read'), array('raise_exception' => false));
303 final XmlRpcClient models = new XmlRpcClient() {{
304 setConfig(new XmlRpcClientConfigImpl() {{
305 setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
308 models.execute("execute_kw", asList(
310 "res.partner", "check_access_rights",
312 new HashMap() {{ put("raise_exception", false); }}
319 .. todo:: this should be runnable and checked
324 Records can be listed and filtered via :meth:`~openerp.models.Model.search`.
326 :meth:`~openerp.models.Model.search` takes a mandatory
327 :ref:`domain <reference/orm/domains>` filter (possibly empty), and returns the
328 database identifiers of all records matching the filter. To list customer
329 companies for instance:
331 .. rst-class:: switchable
333 .. code-block:: python
335 models.execute_kw(db, uid, password,
336 'res.partner', 'search',
337 [[['is_company', '=', True], ['customer', '=', True]]])
341 models.execute_kw(db, uid, password,
342 'res.partner', 'search',
343 [[['is_company', '=', true], ['customer', '=', true]]])
347 $models->execute_kw($db, $uid, $password,
348 'res.partner', 'search', array(
349 array(array('is_company', '=', true),
350 array('customer', '=', true))));
354 asList((Object[])models.execute("execute_kw", asList(
356 "res.partner", "search",
358 asList("is_company", "=", true),
359 asList("customer", "=", true)))
364 [7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74]
369 By default a research will return the ids of all records matching the
370 condition, which may be a huge number. ``offset`` and ``limit`` parameters are
371 available to only retrieve a subset of all matched records.
373 .. rst-class:: switchable
375 .. code-block:: python
377 models.execute_kw(db, uid, password,
378 'res.partner', 'search',
379 [[['is_company', '=', True], ['customer', '=', True]]],
380 {'offset': 10, 'limit': 5})
384 models.execute_kw(db, uid, password,
385 'res.partner', 'search',
386 [[['is_company', '=', true], ['customer', '=', true]]],
387 {offset: 10, limit: 5})
391 $models->execute_kw($db, $uid, $password,
392 'res.partner', 'search',
393 array(array(array('is_company', '=', true),
394 array('customer', '=', true))),
395 array('offset'=>10, 'limit'=>5));
399 asList((Object[])models.execute("execute_kw", asList(
401 "res.partner", "search",
403 asList("is_company", "=", true),
404 asList("customer", "=", true))),
405 new HashMap() {{ put("offset", 10); put("limit", 5); }}
415 Rather than retrieve a possibly gigantic list of records and count them
416 afterwards, :meth:`~openerp.models.Model.search_count` can be used to retrieve
417 only the number of records matching the query. It takes the same
418 :ref:`domain <reference/orm/domains>` filter as
419 :meth:`~openerp.models.Model.search` and no other parameter.
421 .. rst-class:: switchable
423 .. code-block:: python
425 models.execute_kw(db, uid, password,
426 'res.partner', 'search_count',
427 [[['is_company', '=', True], ['customer', '=', True]]])
431 models.execute_kw(db, uid, password,
432 'res.partner', 'search_count',
433 [[['is_company', '=', true], ['customer', '=', true]]])
437 $models->execute_kw($db, $uid, $password,
438 'res.partner', 'search_count',
439 array(array(array('is_company', '=', true),
440 array('customer', '=', true))));
444 (Integer)models.execute("execute_kw", asList(
446 "res.partner", "search_count",
448 asList("is_company", "=", true),
449 asList("customer", "=", true)))
458 calling ``search`` then ``search_count`` (or the other way around) may not
459 yield coherent results if other users are using the server: stored data
460 could have changed between the calls
465 Record data is accessible via the :meth:`~openerp.models.Model.read` method,
466 which takes a list of ids (as returned by
467 :meth:`~openerp.models.Model.search`) and optionally a list of fields to
468 fetch. By default, it will fetch all the fields the current user can read,
469 which tends to be a huge amount.
471 .. rst-class:: switchable
473 .. code-block:: python
475 ids = models.execute_kw(db, uid, password,
476 'res.partner', 'search',
477 [[['is_company', '=', True], ['customer', '=', True]]],
479 [record] = models.execute_kw(db, uid, password,
480 'res.partner', 'read', [ids])
481 # count the number of fields fetched by default
486 ids = models.execute_kw(db, uid, password,
487 'res.partner', 'search',
488 [[['is_company', '=', true], ['customer', '=', true]]],
490 record = models.execute_kw(db, uid, password,
491 'res.partner', 'read', [ids]).first
492 # count the number of fields fetched by default
497 $ids = $models->execute_kw($db, $uid, $password,
498 'res.partner', 'search',
499 array(array(array('is_company', '=', true),
500 array('customer', '=', true))),
502 $records = $models->execute_kw($db, $uid, $password,
503 'res.partner', 'read', array($ids));
504 // count the number of fields fetched by default
509 final List ids = asList((Object[])models.execute(
510 "execute_kw", asList(
512 "res.partner", "search",
514 asList("is_company", "=", true),
515 asList("customer", "=", true))),
516 new HashMap() {{ put("limit", 1); }})));
517 final Map record = (Map)((Object[])models.execute(
518 "execute_kw", asList(
520 "res.partner", "read",
524 // count the number of fields fetched by default
531 Conversedly, picking only three fields deemed interesting.
533 .. rst-class:: switchable
535 .. code-block:: python
537 models.execute_kw(db, uid, password,
538 'res.partner', 'read',
539 [ids], {'fields': ['name', 'country_id', 'comment']})
543 models.execute_kw(db, uid, password,
544 'res.partner', 'read',
545 [ids], {fields: %w(name country_id comment)})
549 $models->execute_kw($db, $uid, $password,
550 'res.partner', 'read',
552 array('fields'=>array('name', 'country_id', 'comment')));
556 asList((Object[])models.execute("execute_kw", asList(
558 "res.partner", "read",
561 put("fields", asList("name", "country_id", "comment"));
567 [{"comment": false, "country_id": [21, "Belgium"], "id": 7, "name": "Agrolait"}]
569 .. note:: even if the ``id`` field is not requested, it is always returned
571 Listing record fields
572 ---------------------
574 :meth:`~openerp.models.Model.fields_get` can be used to inspect
575 a model's fields and check which ones seem to be of interest.
578 it returns a great amount of meta-information (it is also used by client
579 programs) it should be filtered before printing, the most interesting items
580 for a human user are ``string`` (the field's label), ``help`` (a help text if
581 available) and ``type`` (to know which values to expect, or to send when
584 .. rst-class:: switchable
586 .. code-block:: python
589 db, uid, password, 'res.partner', 'fields_get',
590 [], {'attributes': ['string', 'help', 'type']})
595 db, uid, password, 'res.partner', 'fields_get',
596 [], {attributes: %w(string help type)})
600 $models->execute_kw($db, $uid, $password,
601 'res.partner', 'fields_get',
602 array(), array('attributes' => array('string', 'help', 'type')));
606 (Map<String, Map<String, Object>>)models.execute("execute_kw", asList(
608 "res.partner", "fields_get",
611 put("attributes", asList("string", "help", "type"));
623 "property_account_position": {
625 "help": "The fiscal position will determine taxes and accounts used for the partner.",
626 "string": "Fiscal Position"
631 "string": "Signup Token is Valid"
633 "date_localization": {
636 "string": "Geo Localization Date"
641 "string": "Companies that refers to partner"
643 "sale_order_count": {
646 "string": "# of Sales Order"
648 "purchase_order_count": {
651 "string": "# of Purchase Order"
657 Because that is a very common task, Odoo provides a
658 :meth:`~openerp.models.Model.search_read` shortcut which as its name notes is
659 equivalent to a :meth:`~openerp.models.Model.search` followed by a
660 :meth:`~openerp.models.Model.read`, but avoids having to perform two requests
663 Its arguments are similar to :meth:`~openerp.models.Model.search`'s, but it
664 can also take a list of ``fields`` (like :meth:`~openerp.models.Model.read`,
665 if that list is not provided it'll fetch all fields of matched records):
667 .. rst-class:: switchable
669 .. code-block:: python
671 models.execute_kw(db, uid, password,
672 'res.partner', 'search_read',
673 [[['is_company', '=', True], ['customer', '=', True]]],
674 {'fields': ['name', 'country_id', 'comment'], 'limit': 5})
678 models.execute_kw(db, uid, password,
679 'res.partner', 'search_read',
680 [[['is_company', '=', true], ['customer', '=', true]]],
681 {fields: %w(name country_id comment), limit: 5})
685 $models->execute_kw($db, $uid, $password,
686 'res.partner', 'search_read',
687 array(array(array('is_company', '=', true),
688 array('customer', '=', true))),
689 array('fields'=>array('name', 'country_id', 'comment'), 'limit'=>5));
693 asList((Object[])models.execute("execute_kw", asList(
695 "res.partner", "search_read",
697 asList("is_company", "=", true),
698 asList("customer", "=", true))),
700 put("fields", asList("name", "country_id", "comment"));
710 "country_id": [ 21, "Belgium" ],
716 "country_id": [ 76, "France" ],
722 "country_id": [ 233, "United Kingdom" ],
724 "name": "Bank Wealthy and sons"
728 "country_id": [ 105, "India" ],
730 "name": "Best Designers"
734 "country_id": [ 76, "France" ],
744 Records of a model are created using :meth:`~openerp.models.Model.create`. The
745 method will create a single record and return its database identifier.
747 :meth:`~openerp.models.Model.create` takes a mapping of fields to values, used
748 to initialize the record. For any field which has a default value and is not
749 set through the mapping argument, the default value will be used.
751 .. rst-class:: switchable
753 .. code-block:: python
755 id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{
756 'name': "New Partner",
761 id = models.execute_kw(db, uid, password, 'res.partner', 'create', [{
767 $id = $models->execute_kw($db, $uid, $password,
768 'res.partner', 'create',
769 array(array('name'=>"New Partner")));
773 final Integer id = (Integer)models.execute("execute_kw", asList(
775 "res.partner", "create",
776 asList(new HashMap() {{ put("name", "New Partner"); }})
785 while most value types are what would be expected (integer for
786 :class:`~openerp.fields.Integer`, string for :class:`~openerp.fields.Char`
787 or :class:`~openerp.fields.Text`),
789 * :class:`~openerp.fields.Date`, :class:`~openerp.fields.Datetime` and
790 :class:`~openerp.fields.Binary` fields use string values
791 * :class:`~openerp.fields.One2many` and :class:`~openerp.fields.Many2many`
792 use a special command protocol detailed in :meth:`the documentation to
793 the write method <openerp.models.Model.write>`.
798 Reccords can be updated using :meth:`~openerp.models.Model.write`, it takes
799 a list of records to update and a mapping of updated fields to values similar
800 to :meth:`~openerp.models.Model.create`.
802 Multiple records can be updated simultanously, but they will all get the same
803 values for the fields being set. It is not currently possible to perform
804 "computed" updates (where the value being set depends on an existing value of
807 .. rst-class:: switchable
809 .. code-block:: python
811 models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {
812 'name': "Newer partner"
814 # get record name after having changed it
815 models.execute_kw(db, uid, password, 'res.partner', 'name_get', [[id]])
819 models.execute_kw(db, uid, password, 'res.partner', 'write', [[id], {
820 name: "Newer partner"
822 # get record name after having changed it
823 models.execute_kw(db, uid, password, 'res.partner', 'name_get', [[id]])
827 $models->execute_kw($db, $uid, $password, 'res.partner', 'write',
828 array(array($id), array('name'=>"Newer partner")));
829 // get record name after having changed it
830 $models->execute_kw($db, $uid, $password,
831 'res.partner', 'name_get', array(array($id)));
835 models.execute("execute_kw", asList(
837 "res.partner", "write",
840 new HashMap() {{ put("name", "Newer Partner"); }}
843 // get record name after having changed it
844 asList((Object[])models.execute("execute_kw", asList(
846 "res.partner", "name_get",
852 [[78, "Newer partner"]]
857 Records can be deleted in bulk by providing the ids of all records to remove
858 to :meth:`~openerp.models.Model.unlink`.
860 .. rst-class:: switchable
862 .. code-block:: python
864 models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
865 # check if the deleted record is still in the database
866 models.execute_kw(db, uid, password,
867 'res.partner', 'search', [[['id', '=', id]]])
871 models.execute_kw(db, uid, password, 'res.partner', 'unlink', [[id]])
872 # check if the deleted record is still in the database
873 models.execute_kw(db, uid, password,
874 'res.partner', 'search', [[['id', '=', id]]])
878 $models->execute_kw($db, $uid, $password,
879 'res.partner', 'unlink',
881 // check if the deleted record is still in the database
882 $models->execute_kw($db, $uid, $password,
883 'res.partner', 'search',
884 array(array(array('id', '=', $id))));
888 models.execute("execute_kw", asList(
890 "res.partner", "unlink",
891 asList(asList(id))));
892 // check if the deleted record is still in the database
893 asList((Object[])models.execute("execute_kw", asList(
895 "res.partner", "search",
896 asList(asList(asList("id", "=", 78)))
903 Inspection and introspection
904 ----------------------------
906 .. todo:: ``get_external_id`` is kinda crap and may not return an id: it just
907 gets a random existing xid but won't generate one if there is no
908 xid currently associated with the record. And operating with xids
909 isn't exactly fun in RPC.
911 While we previously used :meth:`~openerp.models.Model.fields_get` to query a
912 model's and have been using an arbitrary model from the start, Odoo stores
913 most model metadata inside a few meta-models which allow both querying the
914 system and altering models and fields (with some limitations) on the fly over
917 .. _reference/webservice/inspection/models:
922 Provides informations about Odoo models themselves via its various fields
925 a human-readable description of the model
927 the name of each model in the system
929 whether the model was generated in Python code (``base``) or by creating
930 an ``ir.model`` record (``manual``)
932 list of the model's fields through a :class:`~openerp.fields.One2many` to
933 :ref:`reference/webservice/inspection/fields`
935 :class:`~openerp.fields.One2many` to the :ref:`reference/views` defined
938 :class:`~openerp.fields.One2many` relation to the
939 :ref:`reference/security/acl` set on the model
941 ``ir.model`` can be used to
943 * query the system for installed models (as a precondition to operations
944 on the model or to explore the system's content)
945 * get information about a specific model (generally by listing the fields
947 * create new models dynamically over RPC
951 * "custom" model names must start with ``x_``
952 * the ``state`` must be provided and ``manual``, otherwise the model will
954 * it is not possible to add new *methods* to a custom model, only fields
956 .. rst-class:: force-right
958 a custom model will initially contain only the "built-in" fields available
961 .. rst-class:: switchable
963 .. code-block:: python
965 models.execute_kw(db, uid, password, 'ir.model', 'create', [{
966 'name': "Custom Model",
967 'model': "x_custom_model",
971 db, uid, password, 'x_custom_model', 'fields_get',
972 [], {'attributes': ['string', 'help', 'type']})
977 $db, $uid, $password,
978 'ir.model', 'create', array(array(
979 'name' => "Custom Model",
980 'model' => 'x_custom_model',
985 $db, $uid, $password,
986 'x_custom_model', 'fields_get',
988 array('attributes' => array('string', 'help', 'type'))
995 'ir.model', 'create', [{
996 name: "Custom Model",
997 model: 'x_custom_model',
1000 fields = models.execute_kw(
1001 db, uid, password, 'x_custom_model', 'fields_get',
1002 [], {attributes: %w(string help type)})
1004 .. code-block:: java
1007 "execute_kw", asList(
1009 "ir.model", "create",
1010 asList(new HashMap<String, Object>() {{
1011 put("name", "Custom Model");
1012 put("model", "x_custom_model");
1013 put("state", "manual");
1016 final Object fields = models.execute(
1017 "execute_kw", asList(
1019 "x_custom_model", "fields_get",
1021 new HashMap<String, Object> () {{
1022 put("attributes", asList(
1029 .. code-block:: json
1034 "string": "Created by"
1038 "string": "Created on"
1042 "string": "Last Modified on"
1046 "string": "Last Updated by"
1050 "string": "Last Updated on"
1054 "string": "Display Name"
1062 .. _reference/webservice/inspection/fields:
1067 Provides informations about the fields of Odoo models and allows adding
1068 custom fields without using Python code
1071 :class:`~openerp.fields.Many2one` to
1072 :ref:`reference/webservice/inspection/models` to which the field belongs
1074 the field's technical name (used in ``read`` or ``write``)
1075 ``field_description``
1076 the field's user-readable label (e.g. ``string`` in ``fields_get``)
1078 the :ref:`type <reference/orm/fields>` of field to create
1080 whether the field was created via Python code (``base``) or via
1081 ``ir.model.fields`` (``manual``)
1082 ``required``, ``readonly``, ``translate``
1083 enables the corresponding flag on the field
1085 :ref:`field-level access control <reference/security/fields>`, a
1086 :class:`~openerp.fields.Many2many` to ``res.groups``
1087 ``selection``, ``size``, ``on_delete``, ``relation``, ``relation_field``, ``domain``
1088 type-specific properties and customizations, see :ref:`the fields
1089 documentation <reference/orm/fields>` for details
1091 Like custom models, only new fields created with ``state="manual"`` are
1092 activated as actual fields on the model.
1094 .. warning:: computed fields can not be added via ``ir.model.fields``, some
1095 field meta-information (defaults, onchange) can not be set either
1097 .. todo:: maybe new-API fields could store constant ``default`` in a new
1098 column, maybe JSON-encoded?
1100 .. rst-class:: switchable
1102 .. code-block:: python
1104 id = models.execute_kw(db, uid, password, 'ir.model', 'create', [{
1105 'name': "Custom Model",
1106 'model': "x_custom",
1111 'ir.model.fields', 'create', [{
1118 record_id = models.execute_kw(
1120 'x_custom', 'create', [{
1121 'x_name': "test record",
1123 models.execute_kw(db, uid, password, 'x_custom', 'read', [[record_id]])
1127 $id = $models->execute_kw(
1128 $db, $uid, $password,
1129 'ir.model', 'create', array(array(
1130 'name' => "Custom Model",
1131 'model' => 'x_custom',
1135 $models->execute_kw(
1136 $db, $uid, $password,
1137 'ir.model.fields', 'create', array(array(
1141 'state' => 'manual',
1145 $record_id = $models->execute_kw(
1146 $db, $uid, $password,
1147 'x_custom', 'create', array(array(
1148 'x_name' => "test record"
1151 $models->execute_kw(
1152 $db, $uid, $password,
1154 array(array($record_id)));
1156 .. code-block:: ruby
1158 id = models.execute_kw(
1160 'ir.model', 'create', [{
1161 name: "Custom Model",
1167 'ir.model.fields', 'create', [{
1174 record_id = models.execute_kw(
1176 'x_custom', 'create', [{
1177 x_name: "test record"
1181 'x_custom', 'read', [[record_id]])
1183 .. code-block:: java
1185 final Integer id = (Integer)models.execute(
1186 "execute_kw", asList(
1188 "ir.model", "create",
1189 asList(new HashMap<String, Object>() {{
1190 put("name", "Custom Model");
1191 put("model", "x_custom");
1192 put("state", "manual");
1196 "execute_kw", asList(
1198 "ir.model.fields", "create",
1199 asList(new HashMap<String, Object>() {{
1200 put("model_id", id);
1201 put("name", "x_name");
1202 put("ttype", "char");
1203 put("state", "manual");
1204 put("required", true);
1207 final Integer record_id = (Integer)models.execute(
1208 "execute_kw", asList(
1210 "x_custom", "create",
1211 asList(new HashMap<String, Object>() {{
1212 put("x_name", "test record");
1217 "execute_kw", asList(
1220 asList(asList(record_id))
1223 .. code-block:: json
1227 "create_uid": [1, "Administrator"],
1228 "x_name": "test record",
1229 "__last_update": "2014-11-12 16:32:13",
1230 "write_uid": [1, "Administrator"],
1231 "write_date": "2014-11-12 16:32:13",
1232 "create_date": "2014-11-12 16:32:13",
1234 "display_name": "test record"
1238 Workflow manipulations
1239 ----------------------
1241 :ref:`reference/workflows` can be moved along by sending them *signals*.
1242 Instead of using the top-level ``execute_kw``, signals are sent using
1245 Signals are sent to a specific record, and possibly trigger a transition on
1246 the workflow instance associated with the record.
1248 .. warning:: requires that the ``account`` module be installed
1251 .. rst-class:: switchable
1253 .. code-block:: python
1255 client = models.execute_kw(
1257 'res.partner', 'search_read',
1258 [[('customer', '=', True)]],
1259 {'limit': 1, 'fields': [
1260 'property_account_receivable',
1261 'property_payment_term',
1262 'property_account_position']
1264 invoice_id = models.execute_kw(
1266 'account.invoice', 'create', [{
1267 'partner_id': client['id'],
1268 'account_id': client['property_account_receivable'][0],
1269 'invoice_line': [(0, False, {'name': "AAA"})]
1272 models.exec_workflow(
1273 db, uid, password, 'account.invoice', 'invoice_open', invoice_id)
1277 $client = $models->execute_kw(
1278 $db, $uid, $password,
1279 'res.partner', 'search_read',
1280 array(array(array('customer', '=', true))),
1284 'property_account_receivable',
1285 'property_payment_term',
1286 'property_account_position'
1288 $invoice_id = $models->execute_kw(
1289 $db, $uid, $password,
1290 'account.invoice', 'create', array(array(
1291 'partner_id' => $client['id'],
1292 'account_id' => $client['property_account_receivable'][0],
1293 'invoice_line' => array(array(0, false, array('name' => "AAA")))
1296 $models->exec_workflow(
1297 $db, $uid, $password,
1298 'account.invoice', 'invoice_open',
1301 .. code-block:: ruby
1303 client = models.execute_kw(
1305 'res.partner', 'search_read',
1306 [[['customer', '=', true]]],
1307 {limit: 1, fields: %w(property_account_receivable property_payment_term property_account_position)}
1309 invoice_id = models.execute_kw(
1311 'account.invoice', 'create', [{
1312 partner_id: client['id'],
1313 account_id: client['property_account_receivable'][0],
1314 invoice_line: [[0, false, {name: "AAA"}]]
1317 models.exec_workflow(
1319 'account.invoice', 'invoice_open', invoice_id)
1321 .. code-block:: java
1323 final Map<String, Object> c = (Map<String, Object>)
1324 ((Object[])models.execute("execute_kw", asList(
1326 "res.partner", "search_read",
1329 asList("customer", "=", true))),
1330 new HashMap<String, Object>() {{
1332 put("fields", asList(
1333 "property_account_receivable",
1334 "property_payment_term",
1335 "property_account_position"
1339 final Integer invoice_id = (Integer)models.execute(
1340 "execute_kw", asList(
1342 "account.invoice", "create",
1343 asList(new HashMap<String, Object>() {{
1344 put("partner_id", c.get("id"));
1345 put("account_id", ((Object[])c.get("property_account_receivable"))[0]);
1346 put("invoice_line", asList(
1347 asList(0, false, new HashMap<String, Object>() {{
1355 "exec_workflow", asList(
1357 "account.invoice", "invoice_open", invoice_id));
1362 Available reports can be listed by searching the ``ir.actions.report.xml``
1363 model, fields of interest being
1366 the model on which the report applies, can be used to look for available
1367 reports on a specific model
1369 human-readable report name
1371 the technical name of the report, used to print it
1373 Reports can be printed over RPC with the following information:
1375 * the name of the report (``report_name``)
1376 * the ids of the records to include in the report
1378 .. rst-class:: switchable
1380 .. code-block:: python
1382 invoice_ids = models.execute_kw(
1383 db, uid, password, 'account.invoice', 'search',
1384 [[('type', '=', 'out_invoice'), ('state', '=', 'open')]])
1385 report = xmlrpclib.ServerProxy('{}/xmlrpc/2/report'.format(url))
1386 result = report.render_report(
1387 db, uid, password, 'account.report_invoice', invoice_ids)
1388 report_data = result['result'].decode('base64')
1392 $invoice_ids = $models->execute_kw(
1393 $db, $uid, $password,
1394 'account.invoice', 'search',
1395 array(array(array('type', '=', 'out_invoice'),
1396 array('state', '=', 'open'))));
1397 $report = ripcord::client("$url/xmlrpc/2/report");
1398 $result = $report->render_report(
1399 $db, $uid, $password,
1400 'account.report_invoice', $invoice_ids);
1401 $report_data = base64_decode($result['result']);
1403 .. code-block:: ruby
1406 invoice_ids = models.execute_kw(
1408 'account.invoice', 'search',
1409 [[['type', '=', 'out_invoice'], ['state', '=', 'open']]])
1410 report = XMLRPC::Client.new2("#{url}/xmlrpc/2/report").proxy
1411 result = report.render_report(
1413 'account.report_invoice', invoice_ids)
1414 report_data = Base64.decode64(result['result'])
1416 .. code-block:: java
1418 final Object[] invoice_ids = (Object[])models.execute(
1419 "execute_kw", asList(
1421 "account.invoice", "search",
1423 asList("type", "=", "out_invoice"),
1424 asList("state", "=", "open")))
1426 final XmlRpcClientConfigImpl report_config = new XmlRpcClientConfigImpl();
1427 report_config.setServerURL(
1428 new URL(String.format("%s/xmlrpc/2/report", url)));
1429 final Map<String, Object> result = (Map<String, Object>)client.execute(
1430 report_config, "render_report", asList(
1432 "account.report_invoice",
1434 final byte[] report_data = DatatypeConverter.parseBase64Binary(
1435 (String)result.get("result"));
1440 the report is sent as PDF binary data encoded in base64_, it must be
1441 decoded and may need to be saved to disk before use
1443 .. _PostgreSQL: http://www.postgresql.org
1444 .. _XML-RPC: http://en.wikipedia.org/wiki/XML-RPC
1445 .. _base64: http://en.wikipedia.org/wiki/Base64