Modern Python CLI for Odoo with support for helpdesk, projects, tasks, CRM, and knowledge articles
Project description
Vodoo
A Python library and CLI for Odoo. Use it as a library in your own scripts, services, and automations โ or as a CLI for quick ad-hoc operations and AI-assisted workflows.
Supports helpdesk tickets, project tasks, projects, CRM leads/opportunities, knowledge articles, and timesheets across Odoo 17โ19.
๐ Full Documentation โ Getting started, CLI reference, Python library guide, and API docs.
Quick Start โ Library
from vodoo import OdooClient, OdooConfig, RecordNotFoundError
config = OdooConfig(
url="https://my-instance.odoo.com",
database="mydb",
username="bot@example.com",
password="api-key-or-password",
)
client = OdooClient(config)
# High-level domain helpers
from vodoo.project import list_tasks
tasks = list_tasks(client, limit=10)
# Generic client for any model
partners = client.search_read("res.partner", fields=["name", "email"], limit=5)
# Structured exceptions โ catch what you need
try:
from vodoo.base import get_record
record = get_record(client, "res.partner", 999999999)
except RecordNotFoundError as e:
print(f"{e.model} #{e.record_id} not found")
Quick Start โ CLI
# Run without installing
uvx vodoo crm list --search "acme"
# Or install globally
pip install vodoo
vodoo helpdesk list --stage "In Progress"
vodoo project-task show 42
vodoo timer start 42
Works well with AI assistants like Claude Code โ natural language in, structured Odoo operations out.
Odoo Version Support
| Version | Protocol | Status |
|---|---|---|
| Odoo 17 | Legacy JSON-RPC | โ Fully tested |
| Odoo 18 | Legacy JSON-RPC | โ Fully tested |
| Odoo 19 | JSON-2 (bearer auth) | โ Fully tested |
Auto-detects the Odoo version and selects the appropriate transport. Odoo 19's JSON-2 API is ~3โ4x faster than legacy JSON-RPC.
Features
Library
- ๐ Clean Python API โ
OdooClient,OdooConfig, domain helpers - โก Full async support via
vodoo.aioโAsyncOdooClientwith async context manager - ๐ฏ Structured exception hierarchy mirroring Odoo server errors
- ๐ฆ No CLI dependencies loaded when imported as a library
- ๐ Strict mypy typing throughout
CLI
- ๐ Helpdesk tickets, project tasks, projects, CRM leads, knowledge articles
- โฑ๏ธ Timer / timesheet management (start, stop, status)
- ๐ฌ Comments, internal notes, tags, attachments
- ๐ Search across text fields (name, email, phone, description)
- ๐งฐ Generic CRUD for any Odoo model
- ๐จ Rich terminal output with tables
Shared
- ๐ Auto-detecting transport layer (JSON-2 for Odoo 19+, legacy JSON-RPC for 17โ18)
- โ๏ธ Configuration via environment variables,
.env, orOdooConfig - ๐ HTTPS enforcement warnings for production safety
Installation
# From PyPI
pip install vodoo
# Or run the CLI without installing
uvx vodoo helpdesk list
# From source (development)
git clone https://github.com/julian-r/vodoo.git
cd vodoo
uv sync --all-extras
Configuration
Create a .vodoo.env, ~/.config/vodoo/config.env, or .env file:
ODOO_URL=https://your-odoo-instance.com
ODOO_DATABASE=your_database
ODOO_USERNAME=your_username
ODOO_PASSWORD=your_password_or_api_key
ODOO_DEFAULT_USER_ID=123 # Optional: default user for sudo operations
Or set environment variables directly, or pass values to OdooConfig() in code.
Exception Hierarchy
All exceptions inherit from VodooError so you can catch broadly or narrowly:
VodooError
โโโ ConfigurationError
โโโ AuthenticationError
โโโ RecordNotFoundError โ .model, .record_id attributes
โโโ RecordOperationError
โโโ TransportError โ .code, .data attributes
โ โโโ OdooUserError โ odoo.exceptions.UserError
โ โโโ OdooAccessDeniedError
โ โโโ OdooAccessError
โ โโโ OdooMissingError
โ โโโ OdooValidationError
โโโ FieldParsingError
Server-side Odoo errors are automatically mapped to the matching exception class, so you can handle OdooAccessError separately from OdooValidationError without parsing error strings.
Library Usage
Domain Helpers
Each Odoo model has a dedicated module with high-level functions:
from vodoo import OdooClient, OdooConfig
client = OdooClient(OdooConfig(
url="https://odoo.example.com",
database="prod",
username="bot@example.com",
password="api-key",
))
# Project tasks
from vodoo.project import list_tasks, get_task, add_comment
tasks = list_tasks(client, domain=[("stage_id.name", "=", "In Progress")], limit=20)
task = get_task(client, 42)
add_comment(client, 42, "Deployed to staging")
# CRM leads
from vodoo.crm import list_leads, set_lead_fields
leads = list_leads(client, domain=[("type", "=", "opportunity")], limit=20)
set_lead_fields(client, 123, {"expected_revenue": 50000, "probability": 75})
# Helpdesk (enterprise)
from vodoo.helpdesk import list_tickets
tickets = list_tickets(client, domain=[("stage_id.name", "=", "New")], limit=10)
# Timers
from vodoo.timer import start_timer_on_task, stop_active_timers
start_timer_on_task(client, task_id=42)
stop_active_timers(client)
# Generic CRUD โ works with any Odoo model
records = client.search_read("res.partner", [("is_company", "=", True)], fields=["name"])
new_id = client.create("res.partner", {"name": "Acme Corp", "is_company": True})
client.write("res.partner", [new_id], {"phone": "+1234567890"})
Error Handling
from vodoo import (
OdooClient, OdooConfig, VodooError,
AuthenticationError, RecordNotFoundError,
OdooAccessError, OdooValidationError,
)
try:
client = OdooClient(OdooConfig(...))
client.write("res.partner", [999], {"name": "Updated"})
except AuthenticationError:
print("Bad credentials")
except RecordNotFoundError as e:
print(f"{e.model} #{e.record_id} does not exist")
except OdooAccessError:
print("Insufficient permissions")
except OdooValidationError:
print("Data constraint violated")
except VodooError as e:
print(f"Something else went wrong: {e}")
Async Usage
All library functionality is also available as async via vodoo.aio:
from vodoo import OdooConfig
from vodoo.aio import AsyncOdooClient
config = OdooConfig(
url="https://my-instance.odoo.com",
database="mydb",
username="bot@example.com",
password="api-key",
)
async with AsyncOdooClient(config) as client:
# Domain helpers
from vodoo.aio.project import list_tasks
tasks = await list_tasks(client, limit=10)
# Generic client
partners = await client.search_read("res.partner", fields=["name", "email"], limit=5)
# Comments / notes
from vodoo.aio.crm import add_comment
await add_comment(client, 123, "Async update")
Every sync module has an async counterpart under vodoo.aio โ same function signatures, just awaited.
CLI Usage
CRM Leads/Opportunities
vodoo crm list --search "acme" --type opportunity --stage "Qualified"
vodoo crm show 123
vodoo crm set 123 expected_revenue=50000 probability=75
vodoo crm note 123 "Followed up via phone"
vodoo crm attach 123 proposal.pdf
vodoo crm url 123
Project Tasks
vodoo project-task list --stage "In Progress"
vodoo project-task show 42
vodoo project-task comment 42 "Deployed to staging"
vodoo project-task attach 42 screenshot.png
Projects
vodoo project list
vodoo project show 1
vodoo project note 1 "Sprint planning notes"
Helpdesk Tickets (Enterprise)
vodoo helpdesk list --stage "New" --assigned-to "John"
vodoo helpdesk show 123
vodoo helpdesk note 123 "Internal update"
vodoo helpdesk comment 123 "We're looking into this"
vodoo helpdesk download 456 --output ./attachments/
Knowledge Articles (Enterprise)
vodoo knowledge list --category workspace
vodoo knowledge show 123
vodoo knowledge note 123 "Updated installation section"
Timers / Timesheets
vodoo timer start 42
vodoo timer status
vodoo timer stop
Generic Model Operations
vodoo model read res.partner --domain='[["is_company","=",true]]' --field name --field email
vodoo model create res.partner name="Acme" email=info@acme.com
vodoo model update res.partner 123 phone="+123456789"
vodoo model delete res.partner 123
vodoo model call res.partner name_search --args='["Acme"]'
Security / Service Accounts
vodoo security create-groups
vodoo security assign-bot --login service-vodoo@company.com
For production use, run Vodoo with a dedicated least-privilege service account. See the Security Guide.
Documentation
Full docs at julian-r.github.io/vodoo:
- Getting Started โ Installation, configuration, quick start
- CLI Reference โ All commands with examples
- Library Guide โ Using Vodoo as a Python library
- API Reference โ Auto-generated from docstrings
- Security Guide โ Service account setup
Project Structure
src/vodoo/
โโโ __init__.py # Public API: OdooClient, OdooConfig, exceptions
โโโ exceptions.py # Exception hierarchy (VodooError and subclasses)
โโโ client.py # OdooClient โ delegates to transport layer
โโโ transport.py # Transport abstraction (JSON-2 + legacy JSON-RPC)
โโโ config.py # Pydantic configuration from env/.env files
โโโ auth.py # Authentication and sudo utilities
โโโ base.py # Shared CRUD, messaging, attachment helpers
โโโ main.py # CLI entry point (Typer) โ not loaded by library imports
โโโ helpdesk.py # Helpdesk ticket operations (enterprise)
โโโ project.py # Project task operations
โโโ project_project.py # Project operations
โโโ crm.py # CRM lead/opportunity operations
โโโ knowledge.py # Knowledge article operations (enterprise)
โโโ generic.py # Generic model CRUD
โโโ security.py # Security groups, user management
โโโ timer.py # Timer/timesheet start, stop, status
โโโ aio/ # Async versions of all modules above
โโโ client.py # AsyncOdooClient
โโโ transport.py # Async JSON-2 + legacy transports
โโโ ... # Async domain modules (same API, awaitable)
Integration Tests
60+ tests per Odoo version against real instances in Docker:
./tests/integration/run.sh # All community editions (17, 18, 19)
./tests/integration/run.sh 19 # Just Odoo 19
ENTERPRISE=1 ./tests/integration/run.sh 19 # Include enterprise
Development
uv sync --all-extras
uv run ruff check .
uv run ruff format .
uv run mypy src/vodoo
Publishing
Version is derived from git tags via hatch-vcs:
git tag vX.Y.Z && git push origin vX.Y.Z
GitHub Actions builds and publishes to PyPI automatically.
License
MIT โ see LICENSE. Copyright (c) 2025 Julian Rath.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vodoo-2.0.0.tar.gz.
File metadata
- Download URL: vodoo-2.0.0.tar.gz
- Upload date:
- Size: 159.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a30ffe6e4488fac2fa3601a3c92ef2bb7e716d4ea10e8dbc6914cf7f3fc48039
|
|
| MD5 |
a8645d7cb0ee58728717b6cbe6ee7a4f
|
|
| BLAKE2b-256 |
cedc420aae7205353e807dd046988daa6643006dda480117a8f6e20ee200dfb9
|
Provenance
The following attestation bundles were made for vodoo-2.0.0.tar.gz:
Publisher:
publish.yml on julian-r/vodoo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vodoo-2.0.0.tar.gz -
Subject digest:
a30ffe6e4488fac2fa3601a3c92ef2bb7e716d4ea10e8dbc6914cf7f3fc48039 - Sigstore transparency entry: 953216293
- Sigstore integration time:
-
Permalink:
julian-r/vodoo@48fafbd14db1c2d5c80409171233c65a08b7d563 -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/julian-r
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@48fafbd14db1c2d5c80409171233c65a08b7d563 -
Trigger Event:
release
-
Statement type:
File details
Details for the file vodoo-2.0.0-py3-none-any.whl.
File metadata
- Download URL: vodoo-2.0.0-py3-none-any.whl
- Upload date:
- Size: 81.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98052fcae1be78c9fb5187ba6271b9b0d00e8349697ae591e08e895f7fb64914
|
|
| MD5 |
17c8a9567143d2eeee380aabb0203b80
|
|
| BLAKE2b-256 |
9af7b7ca1b9fb0586cc46415d0a349f3090bfed22df7e24bb86490dd8ddac975
|
Provenance
The following attestation bundles were made for vodoo-2.0.0-py3-none-any.whl:
Publisher:
publish.yml on julian-r/vodoo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vodoo-2.0.0-py3-none-any.whl -
Subject digest:
98052fcae1be78c9fb5187ba6271b9b0d00e8349697ae591e08e895f7fb64914 - Sigstore transparency entry: 953216294
- Sigstore integration time:
-
Permalink:
julian-r/vodoo@48fafbd14db1c2d5c80409171233c65a08b7d563 -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/julian-r
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@48fafbd14db1c2d5c80409171233c65a08b7d563 -
Trigger Event:
release
-
Statement type: