Skip to main content

Helper functions for Odoo server operations via internalized JSON-RPC.

Project description

OdooRPC Toolbox

Language / Sprache: DE | EN

Python License PyPI


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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

odoorpc_toolbox-0.7.0.tar.gz (74.9 kB view details)

Uploaded Source

Built Distribution

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

odoorpc_toolbox-0.7.0-py3-none-any.whl (63.0 kB view details)

Uploaded Python 3

File details

Details for the file odoorpc_toolbox-0.7.0.tar.gz.

File metadata

  • Download URL: odoorpc_toolbox-0.7.0.tar.gz
  • Upload date:
  • Size: 74.9 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

Hashes for odoorpc_toolbox-0.7.0.tar.gz
Algorithm Hash digest
SHA256 bc068754d481f171e11a35671e83f83290c3a1d32f7cddae1d30d7b784500aae
MD5 d97eff0eb41a6e998b37b54ff5e378f6
BLAKE2b-256 d42292aa78fd06bc36437848d19ea0d1b5e8d5416f1070e1d83485e32aac8f04

See more details on using hashes here.

File details

Details for the file odoorpc_toolbox-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: odoorpc_toolbox-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 63.0 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

Hashes for odoorpc_toolbox-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a0d1eb71a35bf15c66d254b9ca788a5acf58b99f87868ea49ce92643c16474d3
MD5 7befb4f46a1ef085d5d814c64f6ccb18
BLAKE2b-256 3b0d4f43c38d70a5be5ae2b1c0d832d54175d4ec50821bacef9214294d9a8558

See more details on using hashes here.

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