Skip to main content

Modern Python CLI for Odoo with support for helpdesk, projects, tasks, CRM, and knowledge articles

Project description

Vodoo

License: MIT Python 3.12+ PyPI Documentation Code style: ruff Type checked: mypy

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 โ€” AsyncOdooClient with 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, or OdooConfig
  • ๐Ÿ” 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:

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.

Built with Typer, Rich, Pydantic, uv, Ruff, and mypy.

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

vodoo-2.0.0.tar.gz (159.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vodoo-2.0.0-py3-none-any.whl (81.2 kB view details)

Uploaded Python 3

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

Hashes for vodoo-2.0.0.tar.gz
Algorithm Hash digest
SHA256 a30ffe6e4488fac2fa3601a3c92ef2bb7e716d4ea10e8dbc6914cf7f3fc48039
MD5 a8645d7cb0ee58728717b6cbe6ee7a4f
BLAKE2b-256 cedc420aae7205353e807dd046988daa6643006dda480117a8f6e20ee200dfb9

See more details on using hashes here.

Provenance

The following attestation bundles were made for vodoo-2.0.0.tar.gz:

Publisher: publish.yml on julian-r/vodoo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for vodoo-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 98052fcae1be78c9fb5187ba6271b9b0d00e8349697ae591e08e895f7fb64914
MD5 17c8a9567143d2eeee380aabb0203b80
BLAKE2b-256 9af7b7ca1b9fb0586cc46415d0a349f3090bfed22df7e24bb86490dd8ddac975

See more details on using hashes here.

Provenance

The following attestation bundles were made for vodoo-2.0.0-py3-none-any.whl:

Publisher: publish.yml on julian-r/vodoo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page