# OdooPyClient
Python client library for Odoo using the requests library and JSON-RPC.
## Quick Start
```python
from odoo_client import OdooPyClient
# Connect to Odoo
odoo = OdooPyClient(
host='http://localhost',
port=8069,
database='odoo',
username='admin',
password='admin'
)
# Authenticate
odoo.authenticate()
# Search and read records
products = odoo.search_read(
model='product.product',
domain=[['sale_ok', '=', True]],
fields=['name', 'lst_price'],
limit=10
)
# Create a record
partner_id = odoo.create(
model='res.partner',
values={'name': 'John Doe', 'email': 'john@example.com'}
)
# Update a record
odoo.update(
model='res.partner',
ids=partner_id,
values={'phone': '+1234567890'}
)
# Delete a record
odoo.delete(model='res.partner', ids=partner_id)
```
## Installation
Install from PyPI:
```bash
pip install odoo-pyclient
```
Or install from source:
```bash
git clone https://github.com/mohamed-helmy/OdooPyClient.git
cd OdooPyClient
pip install -r requirements.txt
```
## Usage
Please refer to the Odoo [API documentation](https://www.odoo.com/documentation/16.0/developer/reference/backend/orm.html) if you need help structuring your database queries.
### Creating Odoo connection instance
Before executing any kind of query operations, a connection instance must be established either with a username/password or with a previously retrieved session id.
```python
from odoo_client import OdooPyClient
odoo = OdooPyClient(
host='http://localhost',
port=8069, # Defaults to 80 if not specified
database='your_database_name',
username='your_username', # Optional if using a stored session_id
password='your_password', # Optional if using a stored session_id
session_id='your_session_id', # Optional if using username/password
context={'lang': 'en_US'}, # Optional - context like language
)
```
### authenticate
Authenticate with the Odoo server. Returns user data including a session id which can be stored for future connections and session persistence.
```python
try:
result = odoo.authenticate()
print(f"Session ID: {odoo.session_id}")
print(f"User ID: {result.get('uid')}")
except Exception as e:
print(f"Authentication failed: {e}")
```
### read
Receives an Odoo database model string and parameters containing the IDs you want to read and the fields you want to retrieve from each result.
Returns a list of results matching the array of IDs provided in the parameters.
```python
# Read partner from server
result = odoo.read(
model='res.partner',
ids=[1, 2, 3, 4, 5],
fields=['name']
)
```
### search_read
Just like the read method, this one receives a model string and parameters. With this method the parameters may include a domain list for filtering purposes (with filter statements similar to SQL's WHERE), limit and offset values for pagination and an order property which can be set to specific fields.
Returns a list of results matching the parameters provided.
```python
result = odoo.search_read(
model='product.product',
domain=[['list_price', '>', '50'], ['list_price', '<', '65']],
fields=['name', 'list_price', 'items'],
order='list_price DESC',
limit=5,
offset=0
)
```
### create
Receives a model string and a values dictionary with properties corresponding to the fields you want to write in the row.
**Example: Create a sale order with order lines**
```python
# Get a customer
partners = odoo.search_read(
model='res.partner',
domain=[['customer_rank', '>', 0]],
fields=['id', 'name'],
limit=1
)
partner_id = partners[0]['id']
# Get products
products = odoo.search_read(
model='product.product',
domain=[['sale_ok', '=', True]],
fields=['id', 'name', 'lst_price'],
limit=2
)
# Create sale order with order lines
sale_order_id = odoo.create(
model='sale.order',
values={
'partner_id': partner_id,
'order_line': [
(0, 0, {
'product_id': products[0]['id'],
'product_uom_qty': 2,
'price_unit': products[0]['lst_price'],
}),
(0, 0, {
'product_id': products[1]['id'],
'product_uom_qty': 3,
'price_unit': products[1]['lst_price'],
}),
],
}
)
print(f"Created sale order with ID: {sale_order_id}")
```
### update
Receives a model string, a list of IDs (or single ID) related to the rows you want to update in the database, and a values dictionary with properties corresponding to the fields that are going to be updated.
If you need to update several rows in the database you can take advantage of Python's concurrent features (see `examples/async_usage.py`) to generate and populate updates in parallel.
**Example: Update a sale order**
```python
result = odoo.update(
model='sale.order',
ids=sale_order_id, # or [1, 2, 3] for multiple IDs
values={
'client_order_ref': 'PO-2025-001',
'note': 'Updated order with client reference',
}
)
print("Sale order updated successfully")
```
### delete
Receives an Odoo database model string and an IDs list (or single ID) corresponding to the rows you want to delete in the database.
**Example: Delete a sale order (after canceling)**
```python
# First, cancel the order (required before deletion)
odoo.rpc_call(
endpoint='/web/dataset/call_kw',
params={
'model': 'sale.order',
'method': 'action_cancel',
'args': [[sale_order_id]],
'kwargs': {'context': odoo.context}
}
)
# Now delete it
result = odoo.delete(
model='sale.order',
ids=sale_order_id
)
print(f"Order {sale_order_id} deleted successfully")
```
### rpc_call (Generic RPC Call)
If you wish to execute a custom RPC call not represented in this library's methods, you can also run a custom call by passing an endpoint string and a params dictionary. This requires understanding how the Odoo Web API works more thoroughly so you can properly structure the function parameters.
**Example 1: Confirm a sale order**
```python
result = odoo.rpc_call(
endpoint='/web/dataset/call_kw',
params={
'model': 'sale.order',
'method': 'action_confirm',
'args': [[sale_order_id]],
'kwargs': {'context': odoo.context}
}
)
print("Sale order confirmed")
```
**Example 2: Search for confirmed orders**
```python
order_ids = odoo.rpc_call(
endpoint='/web/dataset/call_kw',
params={
'model': 'sale.order',
'method': 'search',
'args': [[['state', '=', 'sale']]],
'kwargs': {
'limit': 5,
'order': 'date_order desc',
'context': odoo.context
}
}
)
print(f"Found {len(order_ids)} confirmed sale orders")
```
**Example 3: Count orders by state**
```python
count = odoo.rpc_call(
endpoint='/web/dataset/call_kw',
params={
'model': 'sale.order',
'method': 'search_count',
'args': [[['state', '=', 'draft']]],
'kwargs': {'context': odoo.context}
}
)
print(f"Draft orders: {count}")
```
## Complete Workflow Example
Here's a complete example showing the full lifecycle of a sale order:
```python
from odoo_client import OdooPyClient
# 1. Connect and authenticate
odoo = OdooPyClient(
host='http://localhost',
port=8069,
database='odoo',
username='admin',
password='admin'
)
result = odoo.authenticate()
print(f"Authenticated as user {result['uid']}")
# 2. Create a sale order
partners = odoo.search_read(
model='res.partner',
domain=[['customer_rank', '>', 0]],
fields=['id', 'name'],
limit=1
)
partner_id = partners[0]['id']
products = odoo.search_read(
model='product.product',
domain=[['sale_ok', '=', True]],
fields=['id', 'name', 'lst_price'],
limit=2
)
sale_order_id = odoo.create(
model='sale.order',
values={
'partner_id': partner_id,
'order_line': [
(0, 0, {
'product_id': products[0]['id'],
'product_uom_qty': 2,
'price_unit': products[0]['lst_price'],
})
]
}
)
print(f"Created sale order: {sale_order_id}")
# 3. Read the order
order_data = odoo.read(
model='sale.order',
ids=[sale_order_id],
fields=['name', 'partner_id', 'state', 'amount_total']
)
print(f"Order {order_data[0]['name']}: ${order_data[0]['amount_total']}")
# 4. Update the order
odoo.update(
model='sale.order',
ids=sale_order_id,
values={
'client_order_ref': 'PO-2025-001',
'note': 'Updated via API'
}
)
print("Order updated")
# 5. Confirm the order
odoo.rpc_call(
endpoint='/web/dataset/call_kw',
params={
'model': 'sale.order',
'method': 'action_confirm',
'args': [[sale_order_id]],
'kwargs': {'context': odoo.context}
}
)
print("Order confirmed")
# 6. Search for confirmed orders
order_ids = odoo.rpc_call(
endpoint='/web/dataset/call_kw',
params={
'model': 'sale.order',
'method': 'search',
'args': [[['state', '=', 'sale']]],
'kwargs': {'limit': 5, 'context': odoo.context}
}
)
print(f"Found {len(order_ids)} confirmed orders")
# 7. Cancel and delete (optional)
odoo.rpc_call(
endpoint='/web/dataset/call_kw',
params={
'model': 'sale.order',
'method': 'action_cancel',
'args': [[sale_order_id]],
'kwargs': {'context': odoo.context}
}
)
odoo.delete(model='sale.order', ids=sale_order_id)
print("Order deleted")
```
## Features
- ✅ Authentication with username/password or session ID
- ✅ Session management with cookies
- ✅ CRUD operations (Create, Read, Update, Delete)
- ✅ Search and search_read operations
- ✅ Generic RPC call support
- ✅ Context support (language, timezone, etc.)
## Examples
See the `examples/` directory for more detailed usage examples:
- `example.py` - Complete sale order workflow (create, read, update, delete)
## References
- [Odoo ORM API Reference](https://www.odoo.com/documentation/16.0/developer/reference/backend/orm.html)
- [Odoo Web Service External API](https://www.odoo.com/documentation/16.0/developer/reference/external_api.html)
## License
This project is licensed under the MIT License - see the LICENSE file for details.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## Author
Mohamed Helmy
Raw data
{
"_id": null,
"home_page": "https://github.com/mohamed-helmy/OdooPyClient",
"name": "odoo-pyclient",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "odoo, erp, client, api, jsonrpc",
"author": "Mohamed Helmy",
"author_email": "Mohamed Helmy <helmy419@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/12/84/ad85d81d2532617b218df2f3f0f8c3383742af3bc01abc6eb80d4c5eafd2/odoo_pyclient-1.0.0.tar.gz",
"platform": null,
"description": "# OdooPyClient\n\nPython client library for Odoo using the requests library and JSON-RPC.\n\n## Quick Start\n\n```python\nfrom odoo_client import OdooPyClient\n\n# Connect to Odoo\nodoo = OdooPyClient(\n host='http://localhost',\n port=8069,\n database='odoo',\n username='admin',\n password='admin'\n)\n\n# Authenticate\nodoo.authenticate()\n\n# Search and read records\nproducts = odoo.search_read(\n model='product.product',\n domain=[['sale_ok', '=', True]],\n fields=['name', 'lst_price'],\n limit=10\n)\n\n# Create a record\npartner_id = odoo.create(\n model='res.partner',\n values={'name': 'John Doe', 'email': 'john@example.com'}\n)\n\n# Update a record\nodoo.update(\n model='res.partner',\n ids=partner_id,\n values={'phone': '+1234567890'}\n)\n\n# Delete a record\nodoo.delete(model='res.partner', ids=partner_id)\n```\n\n## Installation\n\nInstall from PyPI:\n\n```bash\npip install odoo-pyclient\n```\n\nOr install from source:\n\n```bash\ngit clone https://github.com/mohamed-helmy/OdooPyClient.git\ncd OdooPyClient\npip install -r requirements.txt\n```\n\n## Usage\n\nPlease refer to the Odoo [API documentation](https://www.odoo.com/documentation/16.0/developer/reference/backend/orm.html) if you need help structuring your database queries.\n\n### Creating Odoo connection instance\n\nBefore executing any kind of query operations, a connection instance must be established either with a username/password or with a previously retrieved session id.\n\n```python\nfrom odoo_client import OdooPyClient\n\nodoo = OdooPyClient(\n host='http://localhost',\n port=8069, # Defaults to 80 if not specified\n database='your_database_name',\n username='your_username', # Optional if using a stored session_id\n password='your_password', # Optional if using a stored session_id\n session_id='your_session_id', # Optional if using username/password\n context={'lang': 'en_US'}, # Optional - context like language\n)\n```\n\n### authenticate\n\nAuthenticate with the Odoo server. Returns user data including a session id which can be stored for future connections and session persistence.\n\n```python\ntry:\n result = odoo.authenticate()\n print(f\"Session ID: {odoo.session_id}\")\n print(f\"User ID: {result.get('uid')}\")\nexcept Exception as e:\n print(f\"Authentication failed: {e}\")\n```\n\n### read\n\nReceives an Odoo database model string and parameters containing the IDs you want to read and the fields you want to retrieve from each result.\n\nReturns a list of results matching the array of IDs provided in the parameters.\n\n```python\n# Read partner from server\nresult = odoo.read(\n model='res.partner',\n ids=[1, 2, 3, 4, 5],\n fields=['name']\n)\n```\n\n### search_read\n\nJust like the read method, this one receives a model string and parameters. With this method the parameters may include a domain list for filtering purposes (with filter statements similar to SQL's WHERE), limit and offset values for pagination and an order property which can be set to specific fields.\n\nReturns a list of results matching the parameters provided.\n\n```python\nresult = odoo.search_read(\n model='product.product',\n domain=[['list_price', '>', '50'], ['list_price', '<', '65']],\n fields=['name', 'list_price', 'items'],\n order='list_price DESC',\n limit=5,\n offset=0\n)\n```\n\n### create\n\nReceives a model string and a values dictionary with properties corresponding to the fields you want to write in the row.\n\n**Example: Create a sale order with order lines**\n\n```python\n# Get a customer\npartners = odoo.search_read(\n model='res.partner',\n domain=[['customer_rank', '>', 0]],\n fields=['id', 'name'],\n limit=1\n)\npartner_id = partners[0]['id']\n\n# Get products\nproducts = odoo.search_read(\n model='product.product',\n domain=[['sale_ok', '=', True]],\n fields=['id', 'name', 'lst_price'],\n limit=2\n)\n\n# Create sale order with order lines\nsale_order_id = odoo.create(\n model='sale.order',\n values={\n 'partner_id': partner_id,\n 'order_line': [\n (0, 0, {\n 'product_id': products[0]['id'],\n 'product_uom_qty': 2,\n 'price_unit': products[0]['lst_price'],\n }),\n (0, 0, {\n 'product_id': products[1]['id'],\n 'product_uom_qty': 3,\n 'price_unit': products[1]['lst_price'],\n }),\n ],\n }\n)\nprint(f\"Created sale order with ID: {sale_order_id}\")\n```\n\n### update\n\nReceives a model string, a list of IDs (or single ID) related to the rows you want to update in the database, and a values dictionary with properties corresponding to the fields that are going to be updated.\n\nIf you need to update several rows in the database you can take advantage of Python's concurrent features (see `examples/async_usage.py`) to generate and populate updates in parallel.\n\n**Example: Update a sale order**\n\n```python\nresult = odoo.update(\n model='sale.order',\n ids=sale_order_id, # or [1, 2, 3] for multiple IDs\n values={\n 'client_order_ref': 'PO-2025-001',\n 'note': 'Updated order with client reference',\n }\n)\nprint(\"Sale order updated successfully\")\n```\n\n### delete\n\nReceives an Odoo database model string and an IDs list (or single ID) corresponding to the rows you want to delete in the database.\n\n**Example: Delete a sale order (after canceling)**\n\n```python\n# First, cancel the order (required before deletion)\nodoo.rpc_call(\n endpoint='/web/dataset/call_kw',\n params={\n 'model': 'sale.order',\n 'method': 'action_cancel',\n 'args': [[sale_order_id]],\n 'kwargs': {'context': odoo.context}\n }\n)\n\n# Now delete it\nresult = odoo.delete(\n model='sale.order',\n ids=sale_order_id\n)\nprint(f\"Order {sale_order_id} deleted successfully\")\n```\n\n### rpc_call (Generic RPC Call)\n\nIf you wish to execute a custom RPC call not represented in this library's methods, you can also run a custom call by passing an endpoint string and a params dictionary. This requires understanding how the Odoo Web API works more thoroughly so you can properly structure the function parameters.\n\n**Example 1: Confirm a sale order**\n\n```python\nresult = odoo.rpc_call(\n endpoint='/web/dataset/call_kw',\n params={\n 'model': 'sale.order',\n 'method': 'action_confirm',\n 'args': [[sale_order_id]],\n 'kwargs': {'context': odoo.context}\n }\n)\nprint(\"Sale order confirmed\")\n```\n\n**Example 2: Search for confirmed orders**\n\n```python\norder_ids = odoo.rpc_call(\n endpoint='/web/dataset/call_kw',\n params={\n 'model': 'sale.order',\n 'method': 'search',\n 'args': [[['state', '=', 'sale']]],\n 'kwargs': {\n 'limit': 5,\n 'order': 'date_order desc',\n 'context': odoo.context\n }\n }\n)\nprint(f\"Found {len(order_ids)} confirmed sale orders\")\n```\n\n**Example 3: Count orders by state**\n\n```python\ncount = odoo.rpc_call(\n endpoint='/web/dataset/call_kw',\n params={\n 'model': 'sale.order',\n 'method': 'search_count',\n 'args': [[['state', '=', 'draft']]],\n 'kwargs': {'context': odoo.context}\n }\n)\nprint(f\"Draft orders: {count}\")\n```\n\n## Complete Workflow Example\n\nHere's a complete example showing the full lifecycle of a sale order:\n\n```python\nfrom odoo_client import OdooPyClient\n\n# 1. Connect and authenticate\nodoo = OdooPyClient(\n host='http://localhost',\n port=8069,\n database='odoo',\n username='admin',\n password='admin'\n)\n\nresult = odoo.authenticate()\nprint(f\"Authenticated as user {result['uid']}\")\n\n# 2. Create a sale order\npartners = odoo.search_read(\n model='res.partner',\n domain=[['customer_rank', '>', 0]],\n fields=['id', 'name'],\n limit=1\n)\npartner_id = partners[0]['id']\n\nproducts = odoo.search_read(\n model='product.product',\n domain=[['sale_ok', '=', True]],\n fields=['id', 'name', 'lst_price'],\n limit=2\n)\n\nsale_order_id = odoo.create(\n model='sale.order',\n values={\n 'partner_id': partner_id,\n 'order_line': [\n (0, 0, {\n 'product_id': products[0]['id'],\n 'product_uom_qty': 2,\n 'price_unit': products[0]['lst_price'],\n })\n ]\n }\n)\nprint(f\"Created sale order: {sale_order_id}\")\n\n# 3. Read the order\norder_data = odoo.read(\n model='sale.order',\n ids=[sale_order_id],\n fields=['name', 'partner_id', 'state', 'amount_total']\n)\nprint(f\"Order {order_data[0]['name']}: ${order_data[0]['amount_total']}\")\n\n# 4. Update the order\nodoo.update(\n model='sale.order',\n ids=sale_order_id,\n values={\n 'client_order_ref': 'PO-2025-001',\n 'note': 'Updated via API'\n }\n)\nprint(\"Order updated\")\n\n# 5. Confirm the order\nodoo.rpc_call(\n endpoint='/web/dataset/call_kw',\n params={\n 'model': 'sale.order',\n 'method': 'action_confirm',\n 'args': [[sale_order_id]],\n 'kwargs': {'context': odoo.context}\n }\n)\nprint(\"Order confirmed\")\n\n# 6. Search for confirmed orders\norder_ids = odoo.rpc_call(\n endpoint='/web/dataset/call_kw',\n params={\n 'model': 'sale.order',\n 'method': 'search',\n 'args': [[['state', '=', 'sale']]],\n 'kwargs': {'limit': 5, 'context': odoo.context}\n }\n)\nprint(f\"Found {len(order_ids)} confirmed orders\")\n\n# 7. Cancel and delete (optional)\nodoo.rpc_call(\n endpoint='/web/dataset/call_kw',\n params={\n 'model': 'sale.order',\n 'method': 'action_cancel',\n 'args': [[sale_order_id]],\n 'kwargs': {'context': odoo.context}\n }\n)\n\nodoo.delete(model='sale.order', ids=sale_order_id)\nprint(\"Order deleted\")\n```\n\n## Features\n\n- \u2705 Authentication with username/password or session ID\n- \u2705 Session management with cookies\n- \u2705 CRUD operations (Create, Read, Update, Delete)\n- \u2705 Search and search_read operations\n- \u2705 Generic RPC call support\n- \u2705 Context support (language, timezone, etc.)\n\n## Examples\n\nSee the `examples/` directory for more detailed usage examples:\n- `example.py` - Complete sale order workflow (create, read, update, delete)\n\n## References\n\n- [Odoo ORM API Reference](https://www.odoo.com/documentation/16.0/developer/reference/backend/orm.html)\n- [Odoo Web Service External API](https://www.odoo.com/documentation/16.0/developer/reference/external_api.html)\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## Author\n\nMohamed Helmy\n",
"bugtrack_url": null,
"license": null,
"summary": "Python client for Odoo using the requests library and JSON-RPC",
"version": "1.0.0",
"project_urls": {
"Bug Reports": "https://github.com/mohamed-helmy/OdooPyClient/issues",
"Homepage": "https://github.com/mohamed-helmy/OdooPyClient",
"Source": "https://github.com/mohamed-helmy/OdooPyClient"
},
"split_keywords": [
"odoo",
" erp",
" client",
" api",
" jsonrpc"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e4f49271056e524c2c5d40a7af9517d34e6a245bdaadd8160c2b1d5c5f761831",
"md5": "f4d806a3592ebb5666d25e8194a30eea",
"sha256": "34be0d2c3593c36fa831c1437ace549f4f3ccde257b93158eb21baac49c3662c"
},
"downloads": -1,
"filename": "odoo_pyclient-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f4d806a3592ebb5666d25e8194a30eea",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 7569,
"upload_time": "2025-10-07T01:24:24",
"upload_time_iso_8601": "2025-10-07T01:24:24.713425Z",
"url": "https://files.pythonhosted.org/packages/e4/f4/9271056e524c2c5d40a7af9517d34e6a245bdaadd8160c2b1d5c5f761831/odoo_pyclient-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "1284ad85d81d2532617b218df2f3f0f8c3383742af3bc01abc6eb80d4c5eafd2",
"md5": "41f359647a2cd5a5d6a9ab2ca0b9b297",
"sha256": "290f4e49369127b0229142a926dbf190f9fd12210c59fd8f423c321a66c56bc0"
},
"downloads": -1,
"filename": "odoo_pyclient-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "41f359647a2cd5a5d6a9ab2ca0b9b297",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 11183,
"upload_time": "2025-10-07T01:24:26",
"upload_time_iso_8601": "2025-10-07T01:24:26.112234Z",
"url": "https://files.pythonhosted.org/packages/12/84/ad85d81d2532617b218df2f3f0f8c3383742af3bc01abc6eb80d4c5eafd2/odoo_pyclient-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-07 01:24:26",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mohamed-helmy",
"github_project": "OdooPyClient",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "requests",
"specs": [
[
">=",
"2.25.0"
]
]
}
],
"lcname": "odoo-pyclient"
}