CLI harness for Odoo 12 ERP — CRUD, modules, DB management, reports via XML-RPC. Requires: running Odoo 12 instance
Project description
cli-anything-odoo_12
CLI harness for Odoo 12 ERP — perform CRUD operations, manage modules, databases, generate reports, export data, and run natural language queries, all from the command line via REST API (muk_rest) or XML-RPC.
Prerequisites
- Python 3.10+
- Running Odoo 12 instance (the real software — HARD DEPENDENCY)
- muk_rest module installed on the Odoo instance (required for REST backend)
# Start Odoo via Docker (typical setup)
cd /path/to/odoo_12
docker-compose up -d
# Odoo should be accessible at http://localhost:8069
Installation
cd agent-harness
pip install -e .
# If using a virtualenv, activate it or add the venv bin to PATH:
export PATH="$(pwd)/.venv/bin:$PATH"
# Or create a symlink to make it available system-wide:
ln -sf "$(pwd)/.venv/bin/cli-anything-odoo_12" ~/.local/bin/cli-anything-odoo_12
# Verify installation
which cli-anything-odoo_12
cli-anything-odoo_12 --help
Quick Start
1. Connect to Odoo
The CLI supports two backend protocols:
| Backend | Flag | Auth | Requirements |
|---|---|---|---|
| REST (default) | --backend rest |
OAuth2 (password or client_credentials) | muk_rest module installed |
| XML-RPC | --backend xmlrpc |
Username/password | None (built-in Odoo) |
Option A: REST API (recommended)
Requires a one-time OAuth2 setup:
Step 1 — Create OAuth2 Client in Odoo:
- Go to Settings > REST API > OAuth2
- Create a new client:
- Name: e.g., "CLI API"
- OAuth Type:
Password Credentials(interactive users) orClient Credentials(service accounts) - User (client_credentials only): select the Odoo user the client will operate as
- Copy the Client Key and Client Secret
Step 2 — Configure the CLI:
# Password grant (interactive user — requires username + password)
cli-anything-odoo_12 config setup-rest \
--client-id <CLIENT_KEY> \
--client-secret <CLIENT_SECRET> \
--grant-type password \
--url http://localhost:8069 \
--db mydb \
--user admin \
--password admin
# Client credentials (service account — uses only client_id/secret)
cli-anything-odoo_12 config setup-rest \
--client-id <CLIENT_KEY> \
--client-secret <CLIENT_SECRET> \
--grant-type client_credentials \
--url http://localhost:8069 \
--db mydb \
--user admin \
--password admin
Step 3 — Verify:
cli-anything-odoo_12 record search res.partner --domain "[('is_company','=',True)]" --limit 5
Option B: XML-RPC (no setup required)
# Connect via XML-RPC and save credentials
cli-anything-odoo_12 connect \
--url http://localhost:8069 --db mydb --user admin --password admin \
--backend xmlrpc --save
After setup (both backends)
# Credentials are reused automatically in all subsequent commands:
cli-anything-odoo_12 record search res.partner --limit 5
cli-anything-odoo_12 ask "empresas"
# Override backend per-command:
cli-anything-odoo_12 --backend xmlrpc record search res.partner --limit 5
# Or pass credentials per-command (overrides saved config):
cli-anything-odoo_12 --url http://localhost:8069 --db mydb --user admin --password admin record search res.partner
2. Record Operations (CRUD)
# Search partners (companies only)
cli-anything-odoo_12 record search res.partner --domain "[('is_company','=',True)]" --fields name,email --limit 10
# Example output:
# Found 10 record(s) in res.partner
# [0]
# id: 1
# name: My Company
# email: info@mycompany.com
# [1]
# id: 14
# name: Azure Interior
# email: azure@example.com
# Read specific record
cli-anything-odoo_12 record read res.partner 1 --fields name,email,phone
# Create record
cli-anything-odoo_12 record create res.partner --values '{"name":"New Company","is_company":true}'
# Output: Created res.partner record id=42
# Update record
cli-anything-odoo_12 record write res.partner 42 --values '{"phone":"+55 11 99999-0000"}'
# Delete record
cli-anything-odoo_12 record unlink res.partner 42
# Count records
cli-anything-odoo_12 record count res.partner --domain "[('is_company','=',True)]"
# Output: res.partner: 15 record(s)
# List model fields
cli-anything-odoo_12 record fields res.partner
# Output: res.partner: 143 fields
3. Module Management
cli-anything-odoo_12 module list --installed
# Output: Found 254 module(s)
cli-anything-odoo_12 module list --search sale
cli-anything-odoo_12 module info sale
cli-anything-odoo_12 module install sale
cli-anything-odoo_12 module update sale
4. Database Operations
cli-anything-odoo_12 db list
# Output: Found 3 database(s)
# - erp-production
# - erp-staging
# - test_cli
cli-anything-odoo_12 db create new_db --lang pt_BR
cli-anything-odoo_12 db backup mydb /tmp/mydb_backup.zip
cli-anything-odoo_12 db restore new_db /tmp/mydb_backup.zip
cli-anything-odoo_12 db drop old_db --confirm
5. Reports
cli-anything-odoo_12 report list --model account.invoice
# Output: Found 63 report(s)
cli-anything-odoo_12 report render account.report_invoice 42 --output invoice.pdf
# Output: Report saved: invoice.pdf (45,231 bytes)
6. Data Export
# Export all partners to CSV
cli-anything-odoo_12 export csv res.partner --output partners.csv --fields name,email,phone
# Output: Exported 920 records to partners.csv
# Export with domain filter
cli-anything-odoo_12 export csv res.partner --output companies.csv --fields name,email --domain "[('is_company','=',True)]"
# Output: Exported 15 records to companies.csv
# Export to JSON with limit
cli-anything-odoo_12 export json res.partner --output partners.json --fields name,email --limit 100
# Output: Exported 100 records to partners.json
# Export to stdout (omit --output)
cli-anything-odoo_12 export json res.partner --fields name --limit 3
7. Natural Language Query (NLQ)
The ask command translates free-text queries (PT-BR, EN, ES) into Odoo search domains using regex patterns. No external dependencies or LLM required.
Simple Queries
# List companies
cli-anything-odoo_12 ask "liste todas as empresas"
# Example output:
# ━━━ Empresas ━━━
# Consulta: res.company | 3 registro(s)
#
# ID Nome E-mail Telefone
# ─── ──────────────────── ────────────────── ──────────
# 1 The Cookiery info@cookiery.com (11) 3333-4444
# 3 Filial Centro centro@cookiery.com —
# 5 Filial Shopping shop@cookiery.com —
#
# Total: 3 registro(s)
# Search with filters
cli-anything-odoo_12 ask "faturas de entrada pagas em janeiro/2024"
# Example output:
# ━━━ Faturas de Entrada — Paid — Janeiro/2024 ━━━
# Consulta: account.invoice | 8 registro(s)
#
# ID Numero Parceiro Data Total (R$) Status
# ──── ──────── ───────────────── ────────── ────────── ──────
# 1042 INV/001 Fornecedor ABC 2024-01-05 15.230,00 paid
# 1043 INV/002 Fornecedor XYZ 2024-01-12 8.500,00 paid
# ...
#
# Total: 8 registro(s) | Soma: R$ 87.430,00
# Active customers with limit
cli-anything-odoo_12 ask "clientes ativos" --limit 10
Cross-Model Grouping (Analytical Queries)
Group orders by product category or individual products — the CLI automatically traverses related models (order > line > product > category).
# POS orders grouped by product category
cli-anything-odoo_12 ask "vendas do pos de janeiro/2026 agrupado por categoria de produto"
# Example output:
# ━━━ Vendas do POS — Janeiro/2026 — por Categoria de Produto ━━━
# Consulta: pos.order | 229 itens de linha
#
# Categoria de Produto Itens Qtd Total (R$) %
# ──────────────────── ───── ──── ────────── ────
# Cookies 98 156 2.376,50 67.4%
# Bebidas 45 89 534,20 15.1%
# Salgados 38 62 312,80 8.9%
# Doces 28 45 198,34 5.6%
# Sem Categoria 20 32 106,00 3.0%
#
# Total: 229 itens | Qtd: 384 | Soma: R$ 3.527,84
# POS orders grouped by individual product
cli-anything-odoo_12 ask "vendas do pos agrupado por produto"
# Invoice grouped by product category
cli-anything-odoo_12 ask "faturas agrupado por categoria de produto"
# Purchase orders grouped by product category
cli-anything-odoo_12 ask "pedidos de compra agrupado por categoria de produto"
Supported Cross-Model Groupings
| Model | Group Key | Traversal Path |
|---|---|---|
| pos.order | categoria de produto |
pos.order > pos.order.line > product.product > product.category |
| pos.order | categoria pos |
pos.order > pos.order.line > product.product > pos.category |
| pos.order | produto |
pos.order > pos.order.line > product.product |
| account.invoice | categoria de produto |
account.invoice > account.invoice.line > product.product > product.category |
| account.invoice | produto |
account.invoice > account.invoice.line > product.product |
| sale.order | categoria de produto |
sale.order > sale.order.line > product.product > product.category |
| sale.order | produto |
sale.order > sale.order.line > product.product |
| purchase.order | categoria de produto |
purchase.order > purchase.order.line > product.product > product.category |
Simple Field Grouping
# POS orders grouped by point-of-sale terminal
cli-anything-odoo_12 ask "vendas do pos agrupado por pdv"
# Example output:
# ━━━ Vendas do POS ━━━
# Consulta: pos.order | 50 registro(s)
#
# Grupo Qtd Total (R$) %
# ───────────────── ─── ────────── ────
# Loja Centro 28 4.520,00 65.2%
# Loja Shopping 22 2.410,00 34.8%
#
# Total: 50 registro(s) | Soma: R$ 6.930,00
With Export
# Export to CSV
cli-anything-odoo_12 ask "faturas de entrada em janeiro/2024 e exporte para csv em /tmp/faturas.csv"
# Output: ... Exportado para /tmp/faturas.csv
# Export to JSON
cli-anything-odoo_12 ask "empresas e exporte para json em /tmp/empresas.json"
# Complex combined query with export
cli-anything-odoo_12 ask "faturas de entrada pagas em janeiro/2024 acima de R$ 5.000 e exporte para csv em /tmp/fat.csv"
Other Query Examples
# Birthdays in a specific month
cli-anything-odoo_12 ask "aniversariantes de marco"
# Date range
cli-anything-odoo_12 ask "faturas de 01/03 a 15/03"
# Relative dates
cli-anything-odoo_12 ask "pedidos de compra este mes"
cli-anything-odoo_12 ask "faturas ultimo mes"
# Amount filters (BRL format)
cli-anything-odoo_12 ask "faturas acima de R$ 10.000"
cli-anything-odoo_12 ask "pedidos de venda abaixo de 5000"
# English queries
cli-anything-odoo_12 ask "invoices above 5000"
cli-anything-odoo_12 ask "active customers"
cli-anything-odoo_12 ask "paid invoices in january/2024"
# Spanish queries
cli-anything-odoo_12 ask "facturas pagadas"
Supported Aliases (Multilingual)
| PT-BR | EN | ES | Odoo Model |
|---|---|---|---|
| empresas | companies | — | res.company |
| parceiros, contatos | partners, contacts | socios | res.partner |
| clientes | customers | — | res.partner (customer=True) |
| fornecedores | suppliers | — | res.partner (supplier=True) |
| faturas, notas fiscais | invoices | facturas | account.invoice |
| pedidos de venda | sale orders | pedido de venta | sale.order |
| pedidos de compra | purchase orders | orden de compra | purchase.order |
| produtos | products | — | product.product |
| pagamentos | payments | — | account.payment |
| vendas do pos, pedidos pos | — | — | pos.order |
| usuarios | users | — | res.users |
| aniversariantes | — | — | res.partner (birthday) |
Supported Filters
| Filter | PT-BR | EN |
|---|---|---|
| Status | pagas, em aberto, rascunho, canceladas, confirmadas | paid, open, draft, cancelled, done |
| Type | de entrada, de saida | incoming, outgoing |
| Date | em janeiro/2024, este mes, ultimo mes | in january/2024, this month, last month |
| Range | de 01/03 a 15/03 | from 01/03 to 15/03 |
| Amount | acima de R$ 10.000, abaixo de 5000 | above 5000, below 1000 |
| Boolean | ativos, inativos, arquivados | active, inactive, archived |
| Document | CPF 123.456.789-00 | — |
| Group | agrupado por categoria de produto | grouped by product category |
| Export | exporte para csv em /tmp/f.csv | — |
| Limit | top 10, limitar a 5 | limit 10 |
8. Configuration Management
# Show saved config (passwords/tokens are masked)
cli-anything-odoo_12 config show
# Example output:
# url: http://localhost:8069
# db: erp-thecookiery
# user: nexuz@nexuz.com.br
# password: adm***
# backend: rest
# grant_type: client_credentials
# client_id: vjVaBgp7...
# client_secret: 4EIwDzsN***
# access_token: eyJhbGci***
# token_expiry: 1742060400.0
# Delete a specific key
cli-anything-odoo_12 config delete --key access_token
# Delete all config
cli-anything-odoo_12 config delete
9. JSON Output (for agents)
# Any command with --json produces machine-readable output
cli-anything-odoo_12 --json record search res.partner --limit 3
# Example output:
# {
# "model": "res.partner",
# "count": 3,
# "records": [
# {"id": 1, "name": "My Company", "email": "info@co.com"},
# ...
# ]
# }
cli-anything-odoo_12 --json ask "empresas"
# Cross-group JSON output
cli-anything-odoo_12 --json ask "vendas do pos agrupado por categoria de produto"
# Example output:
# {
# "model": "pos.order",
# "title": "Vendas do POS",
# "group_by": "Categoria de Produto",
# "groups": [
# {"name": "Cookies", "count": 98, "qty": 156.0, "total": 2376.50, "pct": 67.4},
# ...
# ],
# "grand_count": 229,
# "grand_qty": 384.0,
# "grand_total": 3527.84
# }
cli-anything-odoo_12 --json module list --installed
cli-anything-odoo_12 --json db list
10. Interactive REPL
# Start REPL (just run without subcommand)
cli-anything-odoo_12 --url http://localhost:8069 --db mydb --user admin --password admin
# Example session:
# cli-anything-odoo_12 v1.0.0
# Connected to http://localhost:8069 db=mydb uid=2
#
# odoo_12 [mydb] > record search res.partner --limit 5
# Found 5 record(s) in res.partner
# ...
#
# odoo_12 [mydb] > ask "faturas pagas em janeiro/2024"
# ━━━ Faturas — Paid — Janeiro/2024 ━━━
# ...
#
# odoo_12 [mydb] > quit
Global Flags
| Flag | Effect |
|---|---|
--json |
Machine-readable JSON output (all commands) |
--url |
Odoo server URL (overrides saved config) |
--db |
Database name (overrides saved config) |
--user |
Username (overrides saved config) |
--password |
Password (overrides saved config) |
--backend |
Backend protocol: rest (default) or xmlrpc |
Architecture
cli_anything/odoo_12/
├── odoo_12_cli.py # Main CLI entry point (Click framework)
├── core/
│ ├── aliases.py # Multilingual model/status/type alias registry
│ ├── connection.py # Config management + backend factory (get_rpc)
│ ├── cross_group.py # Cross-model grouping engine
│ ├── database.py # Database operations (list, create, backup, etc.)
│ ├── export.py # CSV/JSON export
│ ├── formatters.py # Business-friendly output formatting
│ ├── module.py # Module management
│ ├── nlq.py # Natural Language Query parser (regex-based)
│ ├── record.py # CRUD operations
│ ├── report.py # Report generation
│ └── session.py # Undo/redo + command history
├── utils/
│ ├── odoo_backend_base.py # ABC OdooBackend (21 abstract methods) + config I/O
│ ├── odoo_backend.py # OdooRPC (XML-RPC) + backward-compat re-exports
│ ├── odoo_rest.py # OdooREST (muk_rest, OAuth2, urllib — zero deps)
│ └── repl_skin.py # Terminal UI (tables, colors, prompts)
└── tests/
├── TEST.md # Test plan + results
├── test_core.py # Unit tests (82 tests, no Odoo needed)
├── test_rest.py # REST backend unit tests (24 tests, mock HTTP)
└── test_full_e2e.py # E2E tests (27 tests, requires running Odoo)
Backend Architecture
All commands work identically regardless of backend. The OdooBackend ABC defines 21 methods
implemented by both OdooRPC (XML-RPC) and OdooREST (muk_rest):
OdooBackend (ABC)
21 abstract methods
/ \
OdooRPC OdooREST
(xmlrpc.client) (urllib — muk_rest)
Built-in Odoo API OAuth2 + JSON/HTTP
The connection.py factory (get_rpc()) selects the backend based on saved config or --backend flag.
REST is the default; XML-RPC is available as fallback via --backend xmlrpc.
OAuth2 Grant Types
| Grant Type | Use Case | Auth Flow |
|---|---|---|
password |
Interactive users | client_id + client_secret + username + password |
client_credentials |
Service accounts / APIs | client_id + client_secret only (user bound to OAuth2 client) |
Token lifecycle is automatic: tokens are persisted in config.json, refreshed on expiry,
and re-authenticated on failure (401 retry).
Troubleshooting
| Problema | Causa | Solucao |
|---|---|---|
Connection refused |
Odoo offline ou URL errada | Verificar se Odoo esta rodando: curl http://localhost:8069/web/login |
Access Denied |
Credenciais invalidas | Verificar usuario/senha: cli-anything-odoo_12 connect --url ... --save |
OAuth2 authentication failed |
client_id/secret incorretos ou grant_type errado | Verificar OAuth2 client no Odoo (Settings > REST API > OAuth2) e grant type |
unauthorized_client |
Grant type do client nao corresponde ao configurado | No Odoo, verificar OAuth Type do client (Password Credentials vs Client Credentials) |
REST API error 500: AccessError |
Usuario REST sem permissoes suficientes | Alterar tipo do usuario para "Usuario Interno" (Settings > Users) |
Modelo nao reconhecido na query |
Alias NLQ nao existe | Usar alias suportado (ver tabela de aliases) ou record search <model> |
command not found |
CLI nao instalado no PATH | Executar pip install -e . no agent-harness/ e verificar which cli-anything-odoo_12 |
404 on /api/* |
muk_rest nao instalado | Instalar modulo muk_rest no Odoo ou usar --backend xmlrpc |
| NLQ retorna 0 registros | Filtros muito restritivos | Simplificar a query (remover filtro de data) e testar incrementalmente |
| Export CSV vazio | Domain sem resultados | Testar a mesma query sem export primeiro para confirmar resultados |
Running Tests
cd agent-harness
pip install -e ".[dev]"
# Unit tests (no Odoo needed)
python3 -m pytest cli_anything/odoo_12/tests/test_core.py -v
# REST backend unit tests (no Odoo needed — uses mock HTTP)
python3 -m pytest cli_anything/odoo_12/tests/test_rest.py -v
# E2E tests (requires running Odoo)
ODOO_URL=http://localhost:8069 ODOO_DB=mydb ODOO_USER=admin ODOO_PASSWORD=admin \
python3 -m pytest cli_anything/odoo_12/tests/test_full_e2e.py -v -s
# All tests
CLI_ANYTHING_FORCE_INSTALLED=1 python3 -m pytest cli_anything/odoo_12/tests/ -v -s
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 cli_anything_odoo_12-1.0.0.tar.gz.
File metadata
- Download URL: cli_anything_odoo_12-1.0.0.tar.gz
- Upload date:
- Size: 57.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8a06b9aedccd9deefad3642617833a6a8d04c1af872ed925d8ba522f3ce0f5de
|
|
| MD5 |
7a112b9e2326e2f49c75d8e4ce07d89e
|
|
| BLAKE2b-256 |
2c12e4244c16c3586430dbe4389db28633d73abd69ed54faabbfe313d3896d23
|
File details
Details for the file cli_anything_odoo_12-1.0.0-py3-none-any.whl.
File metadata
- Download URL: cli_anything_odoo_12-1.0.0-py3-none-any.whl
- Upload date:
- Size: 67.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dac2cc4f184fadf5058b945944e52195cc40a297fcb646a0b795aeca07ede7f4
|
|
| MD5 |
bc7fed44d47e2e5db6c824d564cbce6b
|
|
| BLAKE2b-256 |
cef6de507c2902f7b44af52b63ca69dc1bb39f95763c6e48fc4d201c8abe832c
|