Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Full-featured Odoo 19 ERP connector for OpenClaw - Sales, CRM, Purchase, Inventory, Projects, HR, Fleet, Manufacturing (80+ operations, complete Python code included, XML-RPC integration).
.claude/skills/leoyeai-odoo/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 762% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 188% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 646% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 154% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 285% | 0% |
Full-featured Odoo 19 ERP integration for OpenClaw. Control your entire business via natural language chat commands.
📦 Full Source Code: https://github.com/NullNaveen/openclaw-odoo-skill
\ash npx clawhub install odoo-erp-connector \
The Odoo ERP Connector bridges OpenClaw and Odoo 19, enabling autonomous, chat-driven control over 153+ business modules including:
All operations use smart actions that handle fuzzy matching and auto-creation workflows.
The connector handles fuzzy/incomplete requests with intelligent find-or-create logic.
Example: "Create quotation for Rocky with product Rock"
The system:
ilike matching)This pattern applies across all smart actions:
smart_create_quotation() — customer + productssmart_create_purchase() — vendor + productssmart_create_lead() — partner (optional)smart_create_task() — project + tasksmart_create_employee() — departmentsmart_create_event() — event only (no dependencies)OdooClient — Low-level XML-RPC wrapper
search(), read(), create(), write(), unlink() methodsModel Ops Classes — Business logic for each module
PartnerOps — Customers/suppliersSaleOrderOps — Quotations and sales ordersInvoiceOps — Customer invoicesInventoryOps — Products and stockCRMOps — Leads and opportunitiesPurchaseOrderOps — POs and vendorsProjectOps — Projects and tasksHROps — Employees, departments, expensesManufacturingOps — BOMs and MOsCalendarOps — Events and meetingsFleetOps — Vehicles and odometerEcommerceOps — Website orders and productsSmartActionHandler — High-level natural-language interface
The connector auto-detects required vs. optional fields in Odoo 19:
OdooError with field namejson{ "url": "http://localhost:8069", "db": "your_database", "username": "api_user@yourcompany.com", "api_key": "your_api_key_from_odoo_preferences", "timeout": 60, "max_retries": 3, "poll_interval": 60, "log_level": "INFO", "webhook_port": 8070, "webhook_secret": "" }
config.jsonAlternatively, set in .env:
ODOO_URL=http://localhost:8069
ODOO_DB=your_database
ODOO_USERNAME=api_user@yourcompany.com
ODOO_API_KEY=your_api_keyThe client auto-loads from .env if config.json is missing.
pythonfrom odoo_skill import OdooClient, SmartActionHandler # Load config from config.json client = OdooClient.from_config("config.json") # Test connection status = client.test_connection() print(f"Connected to Odoo {status['server_version']}") # Use smart actions for natural workflows smart = SmartActionHandler(client) # Create a quotation with fuzzy partner and product matching result = smart.smart_create_quotation( customer_name="Rocky", product_lines=[ {"name": "Rock", "quantity": 5, "price_unit": 19.99} ], notes="Fuzzy match quotation" ) print(result["summary"]) # Output: "Created quotation QT-001 for new customer Rocky with 1 × Rock at $19.99"
python# Find-or-create a customer result = smart.find_or_create_partner( name="Acme Corp", is_company=True, city="New York" ) partner = result["partner"] created = result["created"] # Find-or-create a product result = smart.find_or_create_product( name="Widget X", list_price=49.99, type="consu" ) product = result["product"] # Smart quotation (auto-creates customer & products) result = smart.smart_create_quotation( customer_name="Rocky", product_lines=[ {"name": "Product A", "quantity": 10}, {"name": "Product B", "quantity": 5, "price_unit": 25.0} ], notes="Created via smart action" ) order = result["order"] print(f"Order {order['name']} created with {len(result['products'])} product(s)") # Smart lead creation result = smart.smart_create_lead( name="New Prospect", contact_name="John Doe", email="john@prospect.com", expected_revenue=50000.0 ) lead = result["lead"] # Smart task creation (auto-creates project if needed) result = smart.smart_create_task( project_name="Website Redesign", task_name="Fix homepage", description="Update hero section" ) task = result["task"] # Smart employee creation (auto-creates department if needed) result = smart.smart_create_employee( name="Jane Smith", job_title="Developer", department_name="Engineering" ) employee = result["employee"]
pythonfrom odoo_skill.models.sale_order import SaleOrderOps from odoo_skill.models.partner import PartnerOps partners = PartnerOps(client) sales = SaleOrderOps(client) # Get all customers customers = partners.search_customers(limit=10) for cust in customers: print(f"{cust['name']} — {cust.get('email')}") # Create a quotation with specific IDs order = sales.create_quotation( partner_id=42, lines=[ {"product_id": 7, "quantity": 10, "price_unit": 49.99}, {"product_id": 8, "quantity": 5} ], notes="Manual order" ) print(f"Created {order['name']}") # Confirm the order confirmed = sales.confirm_order(order['id']) print(f"Order {confirmed['name']} is now {confirmed['state']}")
All API methods return structured dictionaries:
python{ "summary": "Created quotation QT-001 for new customer Rocky with 1 × Rock", "order": { "id": 1, "name": "QT-001", "state": "draft", "partner_id": [42, "Rocky"], "amount_total": 19.99 }, "customer": { "created": True, "partner": {"id": 42, "name": "Rocky"} }, "products": [ { "created": True, "product": {"id": 7, "name": "Rock"} } ] }
python{ "id": 1, "name": "QT-001", "state": "draft", "partner_id": [42, "Rocky"], "amount_total": 19.99, "order_line": [ { "id": 1, "product_id": [7, "Rock"], "quantity": 1, "price_unit": 19.99, "price_subtotal": 19.99 } ] }
The connector uses custom exceptions:
pythonfrom odoo_skill.errors import OdooError, OdooAuthError, OdooNotFoundError try: result = smart.smart_create_quotation( customer_name="Acme", product_lines=[{"name": "Widget"}] ) except OdooAuthError as e: print(f"Authentication failed: {e}") except OdooNotFoundError as e: print(f"Record not found: {e}") except OdooError as e: print(f"Odoo error: {e}")
The connector supports 153+ installed modules in Odoo 19:
Core
Sales & CRM
Purchasing
Inventory
Accounting
HR
Projects
Manufacturing
Fleet
Marketing
eCommerce
Tools
Plus 50+ more specialized modules
url, db, username, api_key in config.jsonhttp://your-odoo-url/webproduct_tmpl_id, not product_id)name fieldid directlydate_from, date_toUser: "Create a quote for Acme Corp with 10 Widgets at $50 each"
OpenClaw → OdooClient (smart action):
1. Search for customer "Acme Corp"
2. Search for product "Widgets"
3. Create quotation with both
4. Return summary
Result: "✅ Created quotation QT-001 for Acme Corp with 10 × Widgets at $50"User: "Show me the sales pipeline"
OpenClaw → CRMOps.get_pipeline():
- Query all leads/opportunities
- Group by stage
- Calculate total revenue by stage
- Return formatted summary
Result: "Qualified: $50k | Proposal: $100k | Negotiation: $75k | Total: $225k"User: "What products are low on stock?"
OpenClaw → InventoryOps.get_low_stock_products():
- Query products with stock < reorder point
- List each product, stock level, reorder point
- Suggest PO quantities
Result: "Widget X: 5 on hand (min 20) | Component Y: 0 on hand (min 10)"OdooConnector/
├── odoo_skill/
│ ├── client.py # Core OdooClient
│ ├── config.py # Configuration loader
│ ├── errors.py # Custom exceptions
│ ├── retry.py # Retry logic
│ ├── smart_actions.py # Smart action handler
│ ├── models/
│ │ ├── partner.py
│ │ ├── sale_order.py
│ │ ├── invoice.py
│ │ ├── inventory.py
│ │ ├── crm.py
│ │ ├── purchase.py
│ │ ├── project.py
│ │ ├── hr.py
│ │ ├── manufacturing.py
│ │ ├── calendar_ops.py
│ │ ├── fleet.py
│ │ ├── ecommerce.py
│ ├── utils/
│ │ ├── formatting.py # Response formatting
│ │ ├── validators.py # Input validation
│ ├── sync/
│ │ ├── poller.py # Webhook poller
│ │ ├── webhook.py # Webhook handler
├── run_full_test.py # Integration test suite
├── config.json # Configuration (create from template)
├── config.template.json # Configuration template
├── requirements.txt # Python dependencies
├── README.md # User setup guide
├── SKILL.md # This file
└── setup.ps1 # PowerShell installerbash# Run full integration test suite python run_full_test.py # Run single test module python -m pytest tests/test_partners.py -v # Run with coverage python -m pytest --cov=odoo_skill tests/
SmartActionHandler classfind_or_create_* primitives for dependenciessummary, the main record, and creation detailsrun_full_test.pyExample:
pythondef smart_create_invoice(self, customer_name: str, product_lines: list[dict], **kwargs) -> dict: """Create invoice with fuzzy customer and product matching.""" # Find or create customer customer_result = self.find_or_create_partner(customer_name) customer = customer_result["partner"] # Find or create products products = [] for line in product_lines: prod_result = self.find_or_create_product(line["name"], **line) products.append(prod_result) # Create invoice with resolved IDs invoice = self.invoices.create_invoice( partner_id=customer["id"], lines=[...], **kwargs ) return { "summary": f"Created invoice INV-001 for {customer['name']}", "invoice": invoice, "customer": customer_result, "products": products }
This connector is part of the OpenClaw project. For issues, questions, or contributions, contact the development team.
Last Updated: 2026-02-09 Odoo Version: 19.0 Python: 3.10+ Status: Production Ready
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 4,618 | 4,697 | +2% | 1 | 1 | 0% | 729 | 6,287 | +762% | 0 | 0 | — |
case-02 | pass→pass | 9,722 | 9,896 | +2% | 1 | 1 | 0% | 1,654 | 7,265 | +339% | 0 | 0 | — |
case-03 | fail→pass | 12,898 | 7,743 | -40% | 1 | 1 | 0% | 2,341 | 6,739 | +188% | 0 | 0 | — |
case-04 | fail→pass | 5,062 | 4,975 | -2% | 1 | 1 | 0% | 850 | 6,344 | +646% | 0 | 0 | — |
case-05 | pass→pass | 10,229 | 3,369 | -67% | 1 | 1 | 0% | 1,582 | 6,044 | +282% | 0 | 0 | — |
case-06 | fail→pass | 14,341 | 2,136 | -85% | 1 | 1 | 0% | 2,315 | 5,879 | +154% | 0 | 0 | — |
case-07 | fail→pass | 9,307 | 1,801 | -81% | 1 | 1 | 0% | 1,473 | 5,672 | +285% | 0 | 0 | — |
case-08 | pass→pass | 12,659 | 2,741 | -78% | 1 | 1 | 0% | 2,328 | 5,861 | +152% | 0 | 0 | — |
case-09 | pass→pass | 4,543 | 3,842 | -15% | 1 | 1 | 0% | 714 | 5,937 | +732% | 0 | 0 | — |
case-10 | fail→pass | 7,908 | 6,022 | -24% | 1 | 1 | 0% | 1,500 | 6,419 | +328% | 0 | 0 | — |
case-11 | pass→pass | 2,071 | 2,779 | +34% | 1 | 1 | 0% | 336 | 5,862 | +1645% | 0 | 0 | — |
case-12 | fail→pass | 11,578 | 4,817 | -58% | 1 | 1 | 0% | 1,930 | 6,400 | +232% | 0 | 0 | — |
case-13 | pass→pass | 8,606 | 2,098 | -76% | 1 | 1 | 0% | 1,444 | 5,851 | +305% | 0 | 0 | — |
case-14 | pass→pass | 4,519 | 4,425 | -2% | 1 | 1 | 0% | 800 | 6,066 | +658% | 0 | 0 | — |
case-15 | pass→pass | 6,611 | 4,002 | -39% | 1 | 1 | 0% | 1,062 | 6,141 | +478% | 0 | 0 | — |
case-16 | pass→pass | 11,455 | 2,269 | -80% | 1 | 1 | 0% | 1,696 | 5,722 | +237% | 0 | 0 | — |
case-17 | pass→pass | 6,444 | 2,081 | -68% | 1 | 1 | 0% | 1,247 | 5,801 | +365% | 0 | 0 | — |
case-18 | pass→pass | 10,701 | 2,730 | -74% | 1 | 1 | 0% | 1,722 | 5,907 | +243% | 0 | 0 | — |
case-19 | pass→pass | 15,037 | 12,984 | -14% | 1 | 1 | 0% | 2,655 | 7,785 | +193% | 0 | 0 | — |
case-20 | pass→pass | 13,847 | 14,066 | +2% | 1 | 1 | 0% | 1,913 | 7,610 | +298% | 0 | 0 | — |
case-21 | pass→pass | 10,397 | 8,413 | -19% | 1 | 1 | 0% | 1,934 | 6,912 | +257% | 0 | 0 | — |
case-22 | pass→pass | 15,315 | 5,158 | -66% | 1 | 1 | 0% | 2,410 | 6,325 | +162% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.