Helper functions for Odoo server operations via internalized JSON-RPC.
Project description
OdooRPC Toolbox
Deutsche Dokumentation
Projektübersicht
Ein Python-Paket mit Hilfsfunktionen und vollständig internalisierter OdooRPC-Implementierung für Odoo-Server-Kommunikation. JSON-RPC 2.0 Protokoll, MCP-kompatible Introspektion, TTL-Cache, Batch-Writes, native search_read-Optimierung und pluggbare Transport-Schicht mit optionalem HTTP/2 und Retry-Logik.
Autor: Equitania Software GmbH - Pforzheim - Germany Lizenz: GNU Affero General Public License v3 Python: >= 3.10 Version: 0.7.0
Funktionen
- Vollständig internalisiertes OdooRPC - Keine externe Abhängigkeit, JSON-RPC 2.0
- Pluggbare Transport-Schicht (v0.7.0) - urllib (Standard) oder httpx (Connection Pooling, HTTP/2, Retry)
- TTL-Cache für wiederholte Lookups (Länder, Bundesländer, UoMs) - bis zu 99x weniger RPC-Calls
- Batch-Write Context Manager - N Felder → 1 RPC statt N RPCs
- Natives
search_read- 1 RPC statt search + read = 2 RPCs - Request-Metriken - Thread-safe Tracking von Requests, Fehlern und Latenz
- Retry mit Exponential Backoff - Automatische Wiederholung bei 502/503/504
- MCP-kompatible Introspektion - JSON Schema für alle Helper-Methoden
- Erweiterte YAML-Konfiguration mit Transport, Retry, Timeout und Cache Sections
- Config-Generator - CLI-Befehl und Python-Funktion zum Erstellen von Konfigurationsdateien
- Hilfsfunktionen für häufige Odoo-Operationen:
- Partner-Verwaltung (Suchen, Erstellen, Kategorien, Titel)
- Länder- und Bundesland-Abfragen (nach Name oder ISO-Code)
- Produkt-Operationen (Suche nach Referenz, UoM-Lookup)
- Sequenzverwaltung (Lesen/Setzen)
- Dateioperationen (Bilder als BASE64)
- Generische Methodenausführung via
execute_method
Installation
# Standard (nur urllib Transport)
pip install odoorpc-toolbox
# Mit httpx Transport (HTTP/2, Connection Pooling, Retry)
pip install odoorpc-toolbox[httpx]
# Oder mit UV (empfohlen)
uv pip install odoorpc-toolbox[httpx]
Konfiguration
Erstellen Sie eine YAML-Konfigurationsdatei manuell oder per CLI:
# Config-Datei generieren (alle Sections mit Defaults)
odoorpc-init-config
# Benutzerdefinierter Pfad
odoorpc-init-config -o meine_config.yaml
# Minimale Config (nur Server-Section)
odoorpc-init-config --minimal
# Mit benutzerdefinierten Werten
odoorpc-init-config --url http://localhost --port 8069 --database mydb
Oder programmatisch:
from odoorpc_toolbox import generate_config
# Vollständige Config mit allen Sections
generate_config("odoo_config.yaml", url="http://localhost", port=8069, database="mydb")
# Minimale Config (nur Server)
generate_config("odoo_config.yaml", minimal=True)
Basis-Konfiguration
Server:
url: https://odoo.example.com # http:// oder https:// (Protokoll wird automatisch erkannt)
port: 443 # 443 für SSL, 8069 für lokal
user: admin
password: secret
database: mydb
protocol: jsonrpc # jsonrpc oder jsonrpc+ssl
Erweiterte Konfiguration (v0.7.0)
Server:
url: https://odoo.example.com
port: 443
user: admin
password: secret
database: mydb
# Transport Backend (optional)
# Benötigt: pip install odoorpc-toolbox[httpx]
transport:
backend: auto # "auto" (versucht httpx, Fallback urllib), "urllib" oder "httpx"
http2: true # HTTP/2 aktivieren (nur httpx)
pool_connections: 10 # Max. gepoolte Verbindungen (nur httpx)
# Retry-Konfiguration (optional, nur httpx)
retry:
max_attempts: 3 # Maximale Versuche (1 = kein Retry)
backoff_factor: 0.5 # Exponentieller Backoff-Faktor in Sekunden
retry_on: [502, 503, 504] # HTTP-Statuscodes für Retry
# Timeout-Konfiguration (optional)
timeout:
connect: 30 # Verbindungs-Timeout in Sekunden
read: 120 # Lese-Timeout in Sekunden
# Cache-Konfiguration (optional)
cache:
maxsize: 256 # Maximale Cache-Einträge
ttl: 3600 # Time-to-Live in Sekunden
Schnelleinstieg
from odoorpc_toolbox import EqOdooConnection
# Verbindung über YAML-Konfiguration
connection = EqOdooConnection('odoo_config.yaml')
# Native search_read - 1 RPC statt 2
partners = connection.search_read(
"res.partner",
[("is_company", "=", True)],
["name", "email", "city"],
limit=10
)
Partner-Operationen
# Partner erstellen
partner_id = connection.create_partner(
name="Musterfirma GmbH",
is_company=True,
email="info@musterfirma.de",
city="Stuttgart",
zip_code="70173"
)
# Firma suchen
company_id = connection.check_if_company_exists("Musterfirma GmbH", "70173", "Stuttgart")
# Kategorien abrufen oder erstellen
category_id = connection.get_res_partner_category_id("Einzelhandel")
# Partner-Titel
title_id = connection.get_res_partner_title_id("Herr")
Standort-Operationen
# Bundesland-ID (mit automatischem Cache)
state_id = connection.get_state_id(country_id=21, state_name="Bayern")
# Land nach ISO-Code (gecacht)
de_id = connection.get_country_id_by_code("DE")
# Land nach Name
germany_id = connection.get_country_id("Germany")
# Adresse parsen
strasse, hausnr = connection.extract_street_address_part("Hauptstraße 123")
Performance-Optimierungen (v0.6.0)
from odoorpc_toolbox import batch_write
# Batch-Write: 5 Felder → 1 RPC (statt 5 einzelne RPCs)
with batch_write(connection.odoo):
record.name = "Neuer Name"
record.email = "neu@example.com"
record.phone = "+49 711 1234567"
record.street = "Königstraße 1"
record.city = "Stuttgart"
# Cache: 10 identische Lookups → 1 RPC + 9 Cache-Hits
for state in ["Bayern", "Bayern", "Bayern"]:
state_id = connection.get_state_id(21, state) # Nur der erste Aufruf macht einen RPC
# Cache leeren (z.B. nach Datenänderung)
connection.clear_cache()
Transport-Schicht (v0.7.0)
from odoorpc_toolbox import ODOO, create_transport, RetryConfig
# Automatische Backend-Auswahl (httpx wenn verfügbar, sonst urllib)
odoo = ODOO('localhost', port=8069)
# Explizit httpx mit HTTP/2 und Retry
from odoorpc_toolbox import HttpxTransport
transport = HttpxTransport(
http2=True,
retry_config=RetryConfig(max_attempts=3, backoff_factor=0.5),
pool_connections=10,
)
odoo = ODOO('localhost', port=8069, transport=transport)
# Request-Metriken abfragen
from odoorpc_toolbox import MetricsTransport, RequestMetrics
metrics = RequestMetrics()
mt = MetricsTransport(transport, metrics)
odoo = ODOO('localhost', port=8069, transport=mt)
# Nach einigen Operationen...
print(f"Requests: {metrics.total_requests}")
print(f"Fehler: {metrics.total_errors}")
print(f"Durchschnitt: {metrics.avg_time_ms:.1f} ms")
MCP-Introspektion
from odoorpc_toolbox import get_available_methods, print_available_methods
# Alle Methoden als MCP-kompatibles JSON Schema
schema = get_available_methods()
# Menschenlesbare Ausgabe
print_available_methods(format='text')
Sequenz-Verwaltung
# Nächste Sequenznummer lesen (1 RPC)
next_num = connection.get_ir_sequence_number_next_actual("sale.order")
# Sequenznummer setzen (2 RPCs)
connection.set_ir_sequence_number_next_actual("sale.order", 1000)
Direkter ODOO-Zugriff
from odoorpc_toolbox import ODOO
# Direktverbindung ohne YAML
odoo = ODOO('localhost', port=8069)
odoo.login('mydb', 'admin', 'admin')
# Voller Zugriff auf Odoo-Modelle
Partner = odoo.env['res.partner']
partner_ids = Partner.search([('is_company', '=', True)])
Entwicklung
# Setup
uv venv && source .venv/bin/activate
uv pip install -e ".[dev,benchmark]"
# Tests (kein Odoo nötig)
pytest tests/ -m "not integration"
# Qualitätsprüfung
ruff check . && black --check .
Abhängigkeiten
- Runtime: PyYAML >= 6.0 (einzige Pflichtabhängigkeit)
- Optional: httpx[http2] >= 0.25.0 (für HTTP/2, Connection Pooling, Retry)
- OdooRPC: Vollständig internalisiert - keine externe Abhängigkeit
- Python: >= 3.10
English Documentation
Project Overview
A Python package providing helper functions and a fully internalized OdooRPC implementation for Odoo server communication. JSON-RPC 2.0 protocol, MCP-compatible introspection, TTL cache, batch writes, native search_read optimization, and pluggable transport layer with optional HTTP/2 and retry logic.
Author: Equitania Software GmbH - Pforzheim - Germany License: GNU Affero General Public License v3 Python: >= 3.10 Version: 0.7.0
Features
- Fully internalized OdooRPC - No external dependency, JSON-RPC 2.0
- Pluggable transport layer (v0.7.0) - urllib (default) or httpx (connection pooling, HTTP/2, retry)
- TTL cache for repeated lookups (countries, states, UoMs) - up to 99x fewer RPC calls
- Batch write context manager - N fields → 1 RPC instead of N RPCs
- Native
search_read- 1 RPC instead of search + read = 2 RPCs - Request metrics - Thread-safe tracking of requests, errors, and latency
- Retry with exponential backoff - Automatic retry on 502/503/504
- MCP-compatible introspection - JSON Schema for all helper methods
- Extended YAML configuration with transport, retry, timeout, and cache sections
- Config generator - CLI command and Python function for creating configuration files
- Helper functions for common Odoo operations:
- Partner management (search, create, categories, titles)
- Country and state lookups (by name or ISO code)
- Product operations (search by reference, UoM lookup)
- Sequence management (read/set)
- File operations (images as BASE64)
- Generic method execution via
execute_method
Installation
# Standard (urllib transport only)
pip install odoorpc-toolbox
# With httpx transport (HTTP/2, connection pooling, retry)
pip install odoorpc-toolbox[httpx]
# Or with UV (recommended)
uv pip install odoorpc-toolbox[httpx]
Configuration
Create a YAML configuration file manually or via CLI:
# Generate config file (all sections with defaults)
odoorpc-init-config
# Custom path
odoorpc-init-config -o my_config.yaml
# Minimal config (Server section only)
odoorpc-init-config --minimal
# With custom values
odoorpc-init-config --url http://localhost --port 8069 --database mydb
Or programmatically:
from odoorpc_toolbox import generate_config
# Full config with all sections
generate_config("odoo_config.yaml", url="http://localhost", port=8069, database="mydb")
# Minimal config (Server only)
generate_config("odoo_config.yaml", minimal=True)
Basic Configuration
Server:
url: https://odoo.example.com # http:// or https:// (protocol auto-detected)
port: 443 # 443 for SSL, 8069 for local
user: admin
password: secret
database: mydb
protocol: jsonrpc # jsonrpc or jsonrpc+ssl
Extended Configuration (v0.7.0)
Server:
url: https://odoo.example.com
port: 443
user: admin
password: secret
database: mydb
# Transport backend (optional)
# Requires: pip install odoorpc-toolbox[httpx]
transport:
backend: auto # "auto" (tries httpx, falls back to urllib), "urllib", or "httpx"
http2: true # Enable HTTP/2 (httpx only)
pool_connections: 10 # Max pooled connections (httpx only)
# Retry configuration (optional, httpx only)
retry:
max_attempts: 3 # Maximum retry attempts (1 = no retry)
backoff_factor: 0.5 # Exponential backoff base in seconds
retry_on: [502, 503, 504] # HTTP status codes that trigger retry
# Timeout configuration (optional)
timeout:
connect: 30 # Connection timeout in seconds
read: 120 # Read timeout in seconds
# Cache configuration (optional)
cache:
maxsize: 256 # Maximum number of cached entries
ttl: 3600 # Time-to-live in seconds
Quick Start
from odoorpc_toolbox import EqOdooConnection
# Connect via YAML configuration
connection = EqOdooConnection('odoo_config.yaml')
# Native search_read - 1 RPC instead of 2
partners = connection.search_read(
"res.partner",
[("is_company", "=", True)],
["name", "email", "city"],
limit=10
)
Partner Operations
# Create partner
partner_id = connection.create_partner(
name="Acme Corp",
is_company=True,
email="info@acme.com",
city="Stuttgart",
zip_code="70173"
)
# Check if company exists
company_id = connection.check_if_company_exists("Acme Corp", "70173", "Stuttgart")
# Get or create categories
category_id = connection.get_res_partner_category_id("Retail")
# Partner titles
title_id = connection.get_res_partner_title_id("Mr.")
Location Operations
# State ID (automatically cached)
state_id = connection.get_state_id(country_id=233, state_name="California")
# Country by ISO code (cached)
us_id = connection.get_country_id_by_code("US")
# Country by name
usa_id = connection.get_country_id("United States")
# Parse address
street, house_no = connection.extract_street_address_part("123 Main Street")
Performance Optimizations (v0.6.0)
from odoorpc_toolbox import batch_write
# Batch write: 5 fields → 1 RPC (instead of 5 individual RPCs)
with batch_write(connection.odoo):
record.name = "New Name"
record.email = "new@example.com"
record.phone = "+1 555 1234567"
record.street = "123 Main St"
record.city = "New York"
# Cache: 10 identical lookups → 1 RPC + 9 cache hits
for state in ["California", "California", "California"]:
state_id = connection.get_state_id(233, state) # Only the first call makes an RPC
# Clear cache (e.g., after data changes)
connection.clear_cache()
Transport Layer (v0.7.0)
from odoorpc_toolbox import ODOO, create_transport, RetryConfig
# Automatic backend selection (httpx if available, else urllib)
odoo = ODOO('localhost', port=8069)
# Explicit httpx with HTTP/2 and retry
from odoorpc_toolbox import HttpxTransport
transport = HttpxTransport(
http2=True,
retry_config=RetryConfig(max_attempts=3, backoff_factor=0.5),
pool_connections=10,
)
odoo = ODOO('localhost', port=8069, transport=transport)
# Query request metrics
from odoorpc_toolbox import MetricsTransport, RequestMetrics
metrics = RequestMetrics()
mt = MetricsTransport(transport, metrics)
odoo = ODOO('localhost', port=8069, transport=mt)
# After some operations...
print(f"Requests: {metrics.total_requests}")
print(f"Errors: {metrics.total_errors}")
print(f"Average: {metrics.avg_time_ms:.1f} ms")
MCP Introspection
from odoorpc_toolbox import get_available_methods, print_available_methods
# All methods as MCP-compatible JSON Schema
schema = get_available_methods()
# Human-readable output
print_available_methods(format='text')
Sequence Management
# Read next sequence number (1 RPC)
next_num = connection.get_ir_sequence_number_next_actual("sale.order")
# Set sequence number (2 RPCs)
connection.set_ir_sequence_number_next_actual("sale.order", 1000)
Direct ODOO Access
from odoorpc_toolbox import ODOO
# Direct connection without YAML
odoo = ODOO('localhost', port=8069)
odoo.login('mydb', 'admin', 'admin')
# Full access to Odoo models
Partner = odoo.env['res.partner']
partner_ids = Partner.search([('is_company', '=', True)])
Architecture
OdooConnection (YAML config, auth, transport builder)
└── EqOdooConnection (20+ helper methods, TTL cache, batch write)
├── Partner ops: create_partner, check_if_company_exists, categories, titles
├── Location ops: get_country_id, get_state_id, extract_street_address_part
├── Product ops: get_product_by_ref, get_product_uom_id
├── Sequence ops: get/set_ir_sequence_number_next_actual
├── Generic: execute_method, search_read (native, 1 RPC)
└── Cache: TTLCache (configurable maxsize + TTL, thread-safe)
ODOO (internalized OdooRPC)
├── Environment → Model → Fields (14 descriptors)
├── DB service (dump/restore/create/drop)
├── Report service
├── Session persistence (~/.odoorpcrc)
└── RPC Layer
├── ConnectorJSONRPC / ConnectorJSONRPCSSL
├── Transport (v0.7.0)
│ ├── UrllibTransport (default, no extra deps)
│ └── HttpxTransport (HTTP/2, pooling, retry)
├── MetricsTransport (request tracking decorator)
└── RetryConfig (exponential backoff + jitter)
API Reference
| Method | Description | RPCs |
|---|---|---|
search_read(model, domain, fields, ...) |
Search + read in single call | 1 |
create_partner(name, is_company, ...) |
Create partner/company | 1 |
check_if_company_exists(name, zip, city) |
Find existing company | 1 |
get_state_id(country_id, state_name) |
State lookup (cached) | 1* |
get_country_id(country_name) |
Country by name | 1 |
get_country_id_by_code(code) |
Country by ISO code (cached) | 1* |
get_product_uom_id(uom) |
Unit of measure (cached) | 1* |
get_product_by_ref(default_code) |
Product by internal ref | 1 |
get_product_template_by_ref(code) |
Product template by ref | 1 |
get_res_partner_category_id(name) |
Get/create category (cached) | 1* |
get_res_partner_title_id(title) |
Title lookup (cached) | 1* |
get_ir_sequence_number_next_actual(code) |
Read sequence number | 1 |
set_ir_sequence_number_next_actual(code, val) |
Set sequence number | 2 |
execute_method(model, method, ...) |
Generic method call | 1 |
get_picture(path, max_size_mb) |
Load image as BASE64 | 0 |
extract_street_address_part(street) |
Parse street/house number | 0 |
batch_write(odoo) |
Context manager for batched writes | 1 |
generate_config(path, ...) |
Generate YAML config file | 0 |
* Cached methods: First call = 1 RPC, subsequent calls = 0 RPCs (cache hit)
CLI Commands
| Command | Description |
|---|---|
odoorpc-init-config |
Generate YAML config file with all sections |
odoorpc-init-config -o path.yaml |
Custom output path |
odoorpc-init-config --minimal |
Server section only |
odoorpc-init-config --force |
Overwrite existing file |
Development
# Setup
uv venv && source .venv/bin/activate
uv pip install -e ".[dev,benchmark]"
# Unit tests (no Odoo required) - 227 tests
pytest tests/ -m "not integration"
# Integration tests (live Odoo required) - 60 tests
ODOO_TEST_CONFIG=yaml_examples/test_config.yaml pytest tests/integration/ -v
# Benchmarks - 16 scenarios
ODOO_TEST_CONFIG=yaml_examples/test_config.yaml pytest benchmarks/ -v
# Quality checks
ruff check . && black --check .
Requirements
- Runtime: PyYAML >= 6.0 (only required dependency)
- Optional: httpx[http2] >= 0.25.0 (for HTTP/2, connection pooling, retry)
- OdooRPC: Fully internalized - no external dependency
- Python: >= 3.10
Performance Benchmarks / Leistungsmessungen
| Scenario | v0.5.1 RPCs | v0.6.0+ RPCs | Reduction | Speedup |
|---|---|---|---|---|
| batch_write (5 fields) | 5 | 1 | 80% | 5.27x |
| search_read | 2 | 1 | 50% | 1.88-2.23x |
| cached_lookup (100x) | 100 | 1 | 99% | 101.63x |
| state_lookup (10x) | 10 | 1 | 90% | 8.72x |
| get_sequence | 2 | 1 | 50% | 1.93x |
v0.7.0 transport abstraction introduces zero performance regression.
Contributing / Mitwirken
Contributions are welcome! Please feel free to submit a Pull Request.
Beiträge sind willkommen! Bitte zögern Sie nicht, einen Pull Request einzureichen.
# Development setup
uv venv && source .venv/bin/activate
uv pip install -e ".[dev,benchmark]"
pytest tests/ -m "not integration"
ruff check . && black --check .
License / Lizenz
This project is licensed under the GNU Affero General Public License v3 - see the LICENSE.txt file for details.
Dieses Projekt ist unter der GNU Affero General Public License v3 lizenziert - siehe LICENSE.txt für Details.
Project details
Release history Release notifications | RSS feed
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 odoorpc_toolbox-0.7.1.tar.gz.
File metadata
- Download URL: odoorpc_toolbox-0.7.1.tar.gz
- Upload date:
- Size: 77.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f51a33f91a50deae17a044a72245e25f7b52ed26d0963b45fdbd9bcba90afaea
|
|
| MD5 |
a123c0c78e4aaf923d47717be18a5d96
|
|
| BLAKE2b-256 |
19526ec1e434ac7606842aed32da2df0045f79b5fbe464c1cede68fda5cfaea1
|
File details
Details for the file odoorpc_toolbox-0.7.1-py3-none-any.whl.
File metadata
- Download URL: odoorpc_toolbox-0.7.1-py3-none-any.whl
- Upload date:
- Size: 63.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e74c534827a31cf6fc8725c276fc27656df0ae3bea646003e944f67f8e76c102
|
|
| MD5 |
0c867d5187ec0eeb9254c45d202b428c
|
|
| BLAKE2b-256 |
ff9ed26be67ecdfaa27dbd682cf531fdba90110e42f5a2195fa6bae93e8144eb
|