Skill: nullnaveen/odoo-connector (version 1.1.2)

--- name: odoo description: 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). repository: https://github.com/NullNaveen/openclaw-odoo-skill --- # Odoo ERP Connector 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 ## Quick Install \ ash npx clawhub install odoo-erp-connector \ ## Overview The Odoo ERP Connector bridges OpenClaw and Odoo 19, enabling autonomous, chat-driven control over 153+ business modules including: - Sales & CRM - Purchasing & Inventory - Invoicing & Accounting - Projects & Task Management - Human Resources - Fleet Management - Manufacturing (MRP) - Calendar & Events - eCommerce All operations use **smart actions** that handle fuzzy matching and auto-creation workflows. ## Capabilities ### Sales & CRM - Create quotations with dynamic line items - Manage sales orders (draft → confirmed → done) - Search and filter orders by status, customer, date range - Create and qualify leads and opportunities - Move leads through CRM pipeline stages - View full sales pipeline with revenue forecasting ### Purchasing - Create purchase orders from vendors - Manage PO status (draft → purchase → received) - Receive and validate goods - Search and filter POs by vendor, status, date - Track purchase history and vendor performance ### Inventory & Products - Create products (consumables, stockable, services) - Query stock levels and availability - Set reorder points and receive low-stock alerts - Search products by name, code, or category - Track stock movements and valuations ### Invoicing & Accounting - Create and post customer invoices - Manage payment terms and schedules - Query unpaid and overdue invoices - Search by customer, date range, or amount - Track invoice status (draft → posted → paid) ### Projects & Tasks - Create projects and organize by team/status - Create tasks with priority, dates, and assignments - Log timesheets and track project hours - Search and filter tasks by project, status, assignee - Manage project stages and closure ### Human Resources - Create employees and departments - Manage job titles and work schedules - Process expense reports and reimbursements - Search employees by name, department, job - Track leave requests and attendance ### Fleet Management - Create and track vehicles - Log odometer readings and service records - Track maintenance schedules and costs - Search fleet by license plate, status, brand - Generate fleet reports ### Manufacturing (MRP) - Create Bills of Materials (BOMs) - Manage manufacturing orders (MOs) - Track component requirements and production status - Search MOs by product or status - Link BOMs to product variants ### Calendar & Events - Create meetings and events with attendees - Set reminders and locations - Search events by date range or attendee - Track calendar availability ### eCommerce - Publish products to website - View website orders and customer activity - Manage product visibility and pricing ## Command Examples ### Sales - "Create a quotation for Acme Corp with 10 Widgets at $50 each" - "Confirm sales order SO00042" - "Show me all draft quotations from the past week" - "What's the total revenue from completed orders this month?" - "Create a quote for Rocky with product Rock" ### CRM - "Create a lead for Rocky, email [REDACTED_SECRET], potential $50k deal" - "Move lead #47 to Qualified stage" - "Show me the sales pipeline with all open opportunities" - "What leads are at proposal stage?" - "Create an opportunity for Acme with $100k expected value" ### Purchasing - "Create a PO for 500 widgets from Supplier ABC" - "Confirm purchase order PO00123" - "Show all pending purchase orders" - "Get me the vendor history for ABC Supplies" - "What's on order that's overdue?" ### Inventory & Products - "Create a new product: TestWidget, $25 price, min stock 10" - "Show products with stock below 20 units" - "What's the stock level for Widget X?" - "Search for all consumable products" - "Set reorder point for Product Y to 50 units" ### Invoicing - "Create an invoice for Acme Corp with 5 units at $50 each" - "Show me unpaid invoices" - "What invoices are overdue?" - "Post invoice INV-001" - "Send a reminder for invoice INV-002" ### Projects & Tasks - "Create a project called Website Redesign" - "Create a task 'Fix login button' in Website Redesign project" - "Show me all tasks assigned to me" - "Log 3 hours of work on task #42" - "What's the status of the Website Redesign project?" ### HR - "Create employee John Smith, job title Developer" - "Create department Engineering" - "Show me all employees in Engineering" - "Submit expense report for $45.99" - "What are the pending leave requests?" ### Fleet - "Create vehicle: Tesla Model 3, license plate TESLA-001" - "Log odometer reading: 50,000 miles for vehicle #1" - "Show all vehicles with service due" - "What's the maintenance cost for this month?" - "Search for blue vehicles" ### Manufacturing - "Create BOM: Widget contains 3 Components A and 2 Components B" - "Create manufacturing order: produce 50 Widgets" - "Confirm production order #1" - "What's the status of MO-001?" - "Show all in-progress manufacturing orders" ### Calendar - "Create meeting: Team Standup, tomorrow at 10am, 1 hour" - "Show me my meetings for next week" - "What events do I have on the 15th?" - "Schedule a 2-hour planning session with the team" ### eCommerce - "Publish Widget X to the website" - "Show me website orders from this week" - "What's my website revenue?" ## Smart Actions The connector handles fuzzy/incomplete requests with intelligent find-or-create logic. ### How Smart Actions Work **Example:** "Create quotation for Rocky with product Rock" The system: 1. **Searches** for a customer named "Rocky" (case-insensitive, `ilike` matching) 2. **If not found**: Creates a new customer "Rocky" (auto-company flag) 3. **Searches** for product "Rock" 4. **If not found**: Creates a basic product "Rock" (consumable type, default price $0) 5. **Creates** the quotation, linking both the found/created customer and product 6. **Reports** what was found vs. created: - "Created quotation QT-001 for new customer Rocky with 1 × Rock at $0.00" This pattern applies across all smart actions: - `smart_create_quotation()` — customer + products - `smart_create_purchase()` — vendor + products - `smart_create_lead()` — partner (optional) - `smart_create_task()` — project + task - `smart_create_employee()` — department - `smart_create_event()` — event only (no dependencies) ### Benefits - **Fuzzy matching**: Searches are case-insensitive and forgiving - **Auto-creation**: Missing dependencies are created automatically - **Transparency**: Each response explains what was created vs. found - **No IDs needed**: Use names instead of Odoo IDs - **Batch operations**: Create multiple related records in one call ## Architecture ### Core Components **OdooClient** — Low-level XML-RPC wrapper - Connects to Odoo 19 instance - Handles authentication via API key - Provides `search()`, `read()`, `create()`, `write()`, `unlink()` methods - Built-in retry logic and error handling **Model Ops Classes** — Business logic for each module - `PartnerOps` — Customers/suppliers - `SaleOrderOps` — Quotations and sales orders - `InvoiceOps` — Customer invoices - `InventoryOps` — Products and stock - `CRMOps` — Leads and opportunities - `PurchaseOrderOps` — POs and vendors - `ProjectOps` — Projects and tasks - `HROps` — Employees, departments, expenses - `ManufacturingOps` — BOMs and MOs - `CalendarOps` — Events and meetings - `FleetOps` — Vehicles and odometer - `EcommerceOps` — Website orders and products **SmartActionHandler** — High-level natural-language interface - Wraps all Ops classes - Implements find-or-create workflows - Fuzzy name matching (case-insensitive) - Multi-step transaction orchestration - Detailed response summaries ### Field Handling The connector auto-detects required vs. optional fields in Odoo 19: - **Implicit defaults**: Fields with Odoo defaults (e.g., state) are omitted - **Smart creation**: Auto-fills reasonable defaults for optional fields - **Error reporting**: Missing required fields raise clear `OdooError` with field name ## Configuration ### config.json Format ```json { "url": "http://localhost:8069", "db": "your_database", "username": "[REDACTED_SECRET]", "api_key": [REDACTED_SECRET], "timeout": 60, "max_retries": 3, "poll_interval": 60, "log_level": "INFO", "webhook_port": 8070, "webhook_secret": "" } ``` ### Getting Your API Key 1. Log in to your Odoo instance 2. Go to **Settings** → **Users & Companies** → **Users** 3. Open your user record 4. Scroll to **Access Tokens** 5. Click **Generate Token** 6. Copy the token and paste into `config.json` ### Environment Variables Alternatively, set in `.env`: ``` ODOO_URL=http://localhost:8069 ODOO_DB=your_database ODOO_USERNAME=[REDACTED_SECRET] ODOO_API_KEY=your_api_key ``` The client auto-loads from `.env` if `config.json` is missing. ## Python API ### Basic Usage ```python from 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" ``` ### Smart Actions API ```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="[REDACTED_SECRET]", 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"] ``` ### Low-Level Ops API ```python from 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']}") ``` ## Response Format All API methods return structured dictionaries: ### Smart Action Response ```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"} } ] } ``` ### Standard Response ```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 } ] } ``` ## Error Handling The connector uses custom exceptions: ```python from 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}") ``` ## Supported Odoo Modules The connector supports 153+ installed modules in Odoo 19: **Core** - base, web, website **Sales & CRM** - sale, crm, sale_management, website_sale, event, survey **Purchasing** - purchase, purchase_stock, purchase_requisition **Inventory** - stock, stock_intrastat, stock_dropshipping **Accounting** - account, account_accountant, account_analytic, account_payment **HR** - hr, hr_attendance, hr_expense, hr_contract, hr_holidays, hr_org_chart **Projects** - project, project_enterprise, task_base, project_timesheet_forecast **Manufacturing** - mrp, mrp_byproduct, quality, batch, shelf_life **Fleet** - fleet, maintenance **Marketing** - marketing_automation, email_marketing, mass_mailing, sms, website_form **eCommerce** - website_sale, website_sale_analytics, website_sale_comparison, website_form_project **Tools** - calendar, documents, spreadsheet, discuss, mail, knowledge **Plus 50+ more specialized modules** ## Limits & Constraints - **Search limit**: 100 records by default (configurable) - **Timeout**: 60 seconds per request (configurable) - **Retries**: 3 automatic retries on network failure - **Concurrency**: Single-threaded; queue requests if needed - **Rate limiting**: Follow your Odoo instance's API limits ## Troubleshooting ### Connection Issues - Verify `url`, `db`, `username`, `api_key` in config.json - Check Odoo server is running: `http://your-odoo-url/web` - Ensure API key is generated in Odoo user settings - Check network connectivity and firewall rules ### Authentication Errors - Regenerate API key in Odoo - Verify username (email format) - Check that the user has API access enabled - Ensure database name matches exactly ### Missing Field Errors - Field names must match Odoo 19 exactly (e.g., `product_tmpl_id`, not `product_id`) - Some fields are read-only in Odoo (state, computed fields) - Check Odoo model definition: Settings → Technical → Database Structure → Models ### Smart Action Issues - Fuzzy matching is case-insensitive but searches only the `name` field - For exact matching, use the low-level Ops API with `id` directly - If a name exists in multiple records, the first match is used ### Performance - Large searches (limit > 100) may timeout - Use date range filters: `date_from`, `date_to` - Consider batch operations for bulk data ## Examples in OpenClaw ### Natural Language Sales Order ``` User: "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" ``` ### Pipeline Status Check ``` 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" ``` ### Inventory Alert ``` 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)" ``` ## Development ### Project Structure ``` 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 installer ``` ### Running Tests ```bash # 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/ ``` ### Adding a New Smart Action 1. Implement the method in `SmartActionHandler` class 2. Use `find_or_create_*` primitives for dependencies 3. Return a dict with `summary`, the main record, and creation details 4. Add docstring with example usage 5. Test with `run_full_test.py` Example: ```python def 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 } ``` ## License & Support 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

path: package.json
content: {
  "name": "odoo-erp-connector",
  "version": "1.1.0",
  "description": "Full-featured Odoo 19 ERP integration for OpenClaw - Sales, CRM, Purchase, Inventory, Projects, HR, Fleet, Manufacturing (80+ operations, complete Python code included)",
  "author": "NullNaveen",
  "license": "MIT",
  "keywords": [
    "odoo",
    "erp",
    "business",
    "sales",
    "crm",
    "inventory",
    "purchase",
    "hr",
    "fleet",
    "manufacturing",
    "xml-rpc"
  ],
  "engines": {
    "openclaw": ">=2026.2.0"
  },
  "dependencies": {},
  "repository": {
    "type": "git",
    "url": "https://github.com/nullnaveen/openclaw-odoo-skill"
  }
}


path: README.md
content: # OpenClaw Odoo ERP Connector

Full-featured Odoo 19 ERP integration for OpenClaw. Control your entire business via natural language chat commands.

[![ClawHub](https://img.shields.io/badge/ClawHub-odoo--erp--connector-blue)](https://clawhub.ai/skills/odoo-erp-connector)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

## Features

- **80+ operations** across 13 Odoo modules
- **Smart actions** with fuzzy matching and auto-creation
- **Zero dependencies** — pure Python with built-in XML-RPC
- **100% test coverage** — 73 comprehensive tests
- **Production ready** — Thread-safe, retry logic, error handling

### Supported Modules

- 📊 Sales & CRM (quotations, orders, leads, opportunities)
- 🛒 Purchasing (POs, vendors, receipts)
- 📦 Inventory (products, stock levels, alerts)
- 💰 Invoicing (customer invoices, payments)
- 📋 Projects & Tasks (management, timesheets)
- 👥 HR (employees, departments, expenses, leave)
- 🚗 Fleet (vehicles, odometer, maintenance)
- 🏭 Manufacturing (BOMs, production orders)
- 📅 Calendar (events, meetings)
- 🛍️ eCommerce (website orders, product publishing)

## Quick Install

### Via ClawHub (Recommended)

```bash
npx clawhub install odoo-erp-connector
```

### Manual Install

```bash
# Clone the repository
git clone https://github.com/nullnaveen/openclaw-odoo-skill.git
cd openclaw-odoo-skill

# Windows: Run installer
.\setup.ps1

# OR copy manually to OpenClaw skills directory
# Windows: %APPDATA%\npm\node_modules\openclaw\skills\odoo-erp-connector\
# Mac/Linux: ~/.local/share/openclaw/skills/odoo-erp-connector/
```

## Configuration

Create a file named `config.json` in the skill directory with your Odoo credentials:

```json
{
  "url": "http://your-odoo-server:8069",
  "db": "your_database_name",
  "username": "[REDACTED_SECRET]",
  "api_key": "your_odoo_api_key"
}
```

**Note:** For local Odoo installations, use `http://localhost:8069` (or your port).
For Odoo.com/SaaS, use `https://yourcompany.odoo.com`.

### Get Your API Key

1. Log in to Odoo
2. Go to **Settings** → **Users & Companies** → **Users**
3. Open your user record
4. Scroll to **Access Tokens**
5. Click **Generate**
6. Copy the key into `config.json`

## Usage Examples

### Sales
```
"Create a quotation for Acme Corp with 10 Widgets at $50 each"
"Confirm sales order SO00042"
"Show me all draft quotations"
```

### CRM
```
"Create a lead for Rocky, email [REDACTED_SECRET], potential $50k deal"
"Move lead #47 to Qualified stage"
"Show me the sales pipeline"
```

### Inventory
```
"What's the stock level for Widget X?"
"Show products with stock below 20 units"
"Create a new product: TestWidget, $25 price, min stock 10"
```

### Projects
```
"Create a project called Website Redesign"
"Create task 'Fix login button' in Website Redesign"
"Log 3 hours on task #42"
```

### HR
```
"Create employee John Smith, job title Developer"
"Show me all employees in Engineering"
"Submit expense report for $45.99"
```

[See SKILL.md for complete command reference with 30+ examples]

## Smart Actions

The connector automatically handles missing dependencies with fuzzy matching:

**Example:** "Create quotation for Rocky with 100 Snake Skins at $10 each"

1. Searches for customer "Rocky" (case-insensitive)
2. If not found → creates new customer "Rocky"
3. Searches for product "Snake Skin"
4. If not found → creates product with $10 price
5. Creates quotation linking both
6. Reports: "✅ Created quotation S00059 for Rocky with 100 × Snake Skin at $1,150"

## Technical Details

### Architecture

- **OdooClient** — Low-level XML-RPC wrapper with authentication
- **Model Ops** — 13 business module classes (Sales, CRM, Purchase, HR, etc.)
- **SmartActionHandler** — High-level natural language interface with find-or-create workflows

### Testing

```bash
# Run full test suite
python run_full_test.py

# Test single module
pytest tests/test_partners.py -v
```

73 tests covering all 13 modules with 100% feature coverage.

### Requirements

- Python 3.10+
- Odoo 19.0 (may work with 17-18 with field adjustments)
- OpenClaw 2026.2.0+
- No external Python dependencies

## Project Structure

```
openclaw-odoo-skill/
├── SKILL.md                  # OpenClaw skill definition
├── README.md                 # This file
├── package.json              # Skill metadata
├── config.json.template      # Configuration template
├── setup.ps1                 # Windows installer
├── requirements.txt          # Python dependencies (none)
├── odoo_skill/               # Python connector package
│   ├── client.py            # XML-RPC client
│   ├── config.py            # Configuration loader
│   ├── errors.py            # Custom exceptions
│   ├── smart_actions.py     # Smart action workflows
│   ├── models/              # 13 module operation classes
│   │   ├── partner.py
│   │   ├── sale_order.py
│   │   ├── crm.py
│   │   ├── purchase.py
│   │   ├── invoice.py
│   │   ├── inventory.py
│   │   ├── project.py
│   │   ├── hr.py
│   │   ├── fleet.py
│   │   ├── manufacturing.py
│   │   ├── calendar_ops.py
│   │   └── ecommerce.py
│   ├── sync/                # Real-time sync modules
│   │   ├── poller.py       # Change detection poller
│   │   └── webhook.py      # Webhook server
│   └── utils/               # Helper utilities
└── tests/                   # Test suite (73 tests)
```

## Documentation

- **SKILL.md** — Complete skill definition with 30+ command examples
- **README.md** — Installation guide and quick start (this file)
- **TEST_RESULTS.md** — Test coverage report

## Security Note

The security scan on ClawHub correctly identifies this skill as having powerful capabilities:

- ✅ Full CRUD operations across Odoo modules (required for ERP management)
- ✅ Webhook server for real-time updates (optional feature)
- ✅ Background polling for change detection (optional feature)
- ✅ XML-RPC network access (required for Odoo communication)

**These are legitimate features for an ERP connector.** The skill contains no malicious code — review the source if you have concerns.

## Troubleshooting

### Connection Failed
- Verify Odoo URL and server is running
- Check database name matches exactly
- Regenerate API key if authentication fails

### Module Not Found
- Ensure Python 3.10+ is installed
- No external dependencies needed (uses built-in `xmlrpc.client`)

### Permission Denied
- Check Odoo user has access to required modules
- Some operations require specific permissions (Admin, Sales Manager, etc.)

## Contributing

Contributions welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Add tests for new features
4. Submit a pull request

## License

MIT License - see LICENSE file for details

## Author

**NullNaveen**

- ClawHub: [@NullNaveen](https://clawhub.ai/users/NullNaveen)
- GitHub: [@nullnaveen](https://github.com/nullnaveen)

## Support

- **Issues:** [GitHub Issues](https://github.com/nullnaveen/openclaw-odoo-skill/issues)
- **ClawHub:** [odoo-erp-connector](https://clawhub.ai/skills/odoo-erp-connector)
- **Discord:** [OpenClaw Community](https://discord.com/invite/clawd)

---

**Version:** 1.0.1  
**Last Updated:** 2026-02-09  
**Status:** Production Ready ✅


path: requirements.txt
content: # Core (stdlib only - no external deps required for core functionality)
# xmlrpc.client — built-in Python
# json — built-in Python
# http.server — built-in Python

# Optional: for .env file support
python-dotenv>=1.0.0

# Testing
pytest>=7.4.0
pytest-cov>=4.1.0
pytest-mock>=3.12.0


path: odoo_skill/errors.py
content: """
Odoo error classes and XML-RPC fault classification.

Provides a hierarchy of exception types that map to Odoo's error categories,
plus a classifier that converts raw XML-RPC faults into the correct type.
"""

import xmlrpc.client
import logging
from typing import Union

logger = logging.getLogger("odoo_skill")


class OdooError(Exception):
    """Base exception for all Odoo-related errors."""

    def __init__(self, message: str, fault_code: int = 0, model: str = "", method: str = ""):
        self.fault_code = fault_code
        self.model = model
        self.method = method
        super().__init__(message)


class OdooConnectionError(OdooError):
    """Network/connection errors — retryable.

    Raised when the Odoo server is unreachable, the connection times out,
    or an XML-RPC protocol error occurs.
    """
    pass


class OdooAuthenticationError(OdooError):
    """Authentication failures — credentials invalid.

    Raised when the API key, username, or database name is incorrect.
    """
    pass


class OdooAccessError(OdooError):
    """Permission/access denied — NOT retryable.

    Raised when the authenticated user lacks the required permissions
    for the attempted operation.
    """
    pass


class OdooValidationError(OdooError):
    """Data validation errors — NOT retryable without fixing the data.

    Raised when Odoo rejects the data due to constraints, required fields,
    or business rule violations.
    """
    pass


class OdooRecordNotFoundError(OdooError):
    """Record does not exist or has been deleted.

    Raised when attempting to read/write/unlink a record that doesn't exist.
    """
    pass


def classify_error(
    exc: Exception,
    model: str = "",
    method: str = "",
) -> OdooError:
    """Classify a raw exception into the appropriate OdooError subclass.

    Examines the fault string from XML-RPC faults and maps known Odoo
    error patterns to our exception hierarchy. Network-level exceptions
    are mapped to OdooConnectionError.

    Args:
        exc: The original exception.
        model: The Odoo model being accessed (for error context).
        method: The Odoo method being called (for error context).

    Returns:
        An OdooError subclass instance with the original message preserved.
    """
    msg = str(exc)

    # XML-RPC faults contain Odoo's internal error type in faultString
    if isinstance(exc, xmlrpc.client.Fault):
        fault_string = exc.faultString or ""
        fault_code = exc.faultCode

        if "AccessDenied" in fault_string:
            return OdooAuthenticationError(
                f"Authentication failed: {fault_string}",
                fault_code=fault_code, model=model, method=method,
            )

        if "AccessError" in fault_string:
            return OdooAccessError(
                f"Permission denied on {model}.{method}: {fault_string}",
                fault_code=fault_code, model=model, method=method,
            )

        if "ValidationError" in fault_string or "UserError" in fault_string:
            return OdooValidationError(
                f"Validation error on {model}.{method}: {fault_string}",
                fault_code=fault_code, model=model, method=method,
            )

        if "MissingError" in fault_string:
            return OdooRecordNotFoundError(
                f"Record not found in {model}: {fault_string}",
                fault_code=fault_code, model=model, method=method,
            )

        # Generic fault
        return OdooError(
            f"Odoo error on {model}.{method}: {fault_string}",
            fault_code=fault_code, model=model, method=method,
        )

    # Network-level errors
    if isinstance(exc, xmlrpc.client.ProtocolError):
        return OdooConnectionError(
            f"Protocol error ({exc.errcode}): {exc.errmsg}",
            model=model, method=method,
        )

    if isinstance(exc, (ConnectionError, TimeoutError, OSError)):
        return OdooConnectionError(
            f"Connection error: {msg}",
            model=model, method=method,
        )

    # Fallback
    return OdooError(
        f"Unexpected error on {model}.{method}: {msg}",
        model=model, method=method,
    )


path: odoo_skill/sync/__init__.py
content: """Sync modules: polling and webhook receiver."""

from .poller import OdooChangePoller
from .webhook import OdooWebhookServer

__all__ = ["OdooChangePoller", "OdooWebhookServer"]


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)