Skip to main content
Glama

odoo-cli

conformance

A command-line client for Odoo Community. It is built on a catalog of business operations, each checked against what Odoo itself does on the stock image. Every command answers in one JSON envelope: the data, or an error with a code, the reason and what to do next. Scripts, notebooks and AI agents can drive Odoo through it as dependably as a person does through the web client.

Odoo is a registered trademark of Odoo S.A. This project is not affiliated with or endorsed by Odoo S.A.

What it covers

1368 operations over the business apps of Odoo Community 19.0. 1301 of them are verified on the stock image. On Odoo Community 20.0, a candidate series (run with --allow-unverified until it is supported), 1187 of the 1306 operations 20.0 can run are verified; the other 62 need models, dialogs or buttons 20.0 removed, and odoo-cli refuses them with the reason.

Kind

Operations

Verified

Read, list, group and paginate

591

589

Create and update

557

543

Business actions (form and list buttons)

177

132

Dialogs (wizards)

27

21

Print a document's PDF

7

7

Guarded deletes

8

8

Install a module

1

1

  • Apps:

    • Contacts, CRM, Sales, Invoicing and accounting, Purchase, Inventory, Manufacturing and Project (recurring tasks included).

    • Employees, Skills, Time Off, Attendances, Expenses and Recruitment.

    • Point of Sale with Restaurant, Calendar, Website, Discuss and To-Do.

    • Fleet, Maintenance, Repairs, Events and Surveys.

    • Purchase agreements, batch transfers, loyalty, expiry dates, shipping, dropshipping, subcontracting, landed costs and margins.

    • Email and SMS Marketing with marketing cards, Live Chat with its chatbots, Lunch, eLearning, eCommerce (public categories, ribbons, wishlists, product feeds, checkout steps) and Data Cleaning.

  • Vietnam: e-invoices through Viettel SInvoice (l10n_vn_edi_viettel).

  • Business as code: odoo-cli spec plan|apply brings an Odoo to a YAML description of a company: settings, products, website pages and more. The same spec rebuilds the same company on a blank database. A team shares a backend on S3, R2, MinIO or a folder: one apply at a time, plans saved for review, every apply recorded, the spec last applied to pull back.

  • Routines: odoo-cli routines runs the checks a business makes every day, week or month (sales yesterday, overdue receivables, late transfers, last month's books review), keeps what Odoo answered and hands it to the agent you chose, on a timer.

  • Change history: who changed which field of a record, when, and from what to what (structure.field_change.list, structure.change_note.list), for the fields a review needs: payee bank accounts, partners' tax numbers, document states and dates, lock dates, prices. Odoo shows it to administrators only; on 20.0 it needs the mail_tracking module.

  • Prints: the PDFs equal the browser's own download, page for page and word for word.

How "verified" is earned. Each operation runs on a lab with the stock Odoo image. Its result is then compared with what Odoo itself reads, writes or prints. The tooling is in conformance/ and the evidence in docs/odoo-surface/evidence/.

Odoo Online and Enterprise. odoo-cli connects to them through the JSON-2 API, and runs their reads with a note that they are unverified. docs/odoo-online-and-enterprise.md says what was tested and what it does not promise.

Check it yourself. The conformance workflow does it in the open, every week and on every release:

  • It builds Odoo 19.0 and 20.0 from the public images pinned by digest in conformance/odoo-conformance-labs.json, with demo data and every module an operation needs.

  • It runs every operation through odoo-cli.

  • It checks each result against Odoo through an independent JSON-RPC reader.

The run fails when an operation the manifest calls verified now disagrees with Odoo. Its evidence is attached to the run and to the release. docs/conformance.md explains:

  • what "agree" means for a read, a write, a button, a dialog and a print;

  • how to read the report;

  • what the check does not prove (your own Odoo's customisations, Enterprise, Odoo Online).

The same script runs on any machine with Docker or Podman, in about an hour per series:

conformance/odoo-conformance-lab.sh --series 20.0 --out /tmp/conformance-20
cat /tmp/conformance-20/summary.md

Every operation carries its readiness: CURRENT_VERIFIED, UNVERIFIED or HISTORICAL_ONLY. An UNVERIFIED operation runs only with --allow-unverified.

Related MCP server: odooclaw-mcp

Install

curl -fsSL https://raw.githubusercontent.com/asao-vn/asao-odoo-tools/main/scripts/install.sh | sh

The installer downloads the release binary for your machine and checks it against the release's checksums. You can also download odoo-cli_<os>_<arch> from Releases, for Linux, macOS or Windows on amd64 or arm64. To build from source, run make build (Go 1.26.6 or newer).

The Python companion, for analysis and scripts:

pip install asao-erp      # drives this odoo-cli; needs odoo-cli on the PATH

Use the command line for one action at a time, agents (MCP) and schedules. Use asao-erp when the work is many reads put together, such as a monthly report, a statistic, or a review into a table. See Python below.

Quick start

# A profile is configuration only: endpoint, database, login and a credential reference.
odoo-cli profile set --name my-odoo --endpoint https://erp.example.com --database erp --login admin@example.com

# The password (or a JSON-2 API key) never goes into the profile: give it through the environment,
# the OS keyring (`odoo-cli credential set --ref my-odoo --login ... --password-stdin`) or a file.
export AERP_CREDENTIAL_MY_ODOO='...'

# What this Odoo is, and which operations it can run.
odoo-cli doctor --profile my-odoo --probe --output json

odoo-cli catalog list --domain sales --output json
odoo-cli execute --profile my-odoo --operation sales.quotation.list --limit 5 --output json
odoo-cli execute --profile my-odoo --operation finance.journal_entry.list \
  --group-by journal_id --aggregate amount_total:sum --output json
odoo-cli execute --profile my-odoo --operation identity.contact.create --set name="An Phat Co." --output json
odoo-cli execute --profile my-odoo --operation finance.invoice.print --provider-target-id 42 --save-file invoice.pdf
  • An Odoo on your own network: odoo-cli refuses loopback, private and link-local addresses by default. To allow your Odoo only, name it as the profile's endpoint writes it, with its port: ASAO_PRIVATE_PROVIDER_ENDPOINTS=192.168.1.10:8069 (several are separated by commas). ASAO_ALLOW_PRIVATE_PROVIDER_ENDPOINTS=true allows every private address.

  • Odoo's JSON-2 API (19.0): use --protocol json2 and an API key as the credential. To print PDFs over JSON-2, also give the web password in AERP_CREDENTIAL_<REF>_WEB, because Odoo serves reports only to a logged-in web session.

  • Interactive help: odoo-cli --help, and odoo-cli <command> --help --output json for one command's contract.

AI agents (MCP)

odoo-cli mcp serve offers the catalog to an AI agent over the Model Context Protocol, on Community as on Enterprise. Each tool is one odoo-cli command, run against the profile you start the server with, with the same checks as a typed command.

claude mcp add odoo -- odoo-cli mcp serve --profile my-odoo                 # reads only
claude mcp add odoo -- odoo-cli mcp serve --profile my-odoo --allow-writes  # and writes
odoo-cli mcp tools --allow-writes --output json                             # what an agent would get
  • Reads by default: 624 tools:

    • the 598 verified reads;

    • catalog discovery, the pre-flight check, resolve (ids only), agents status, the review (review run, review checks, review test-check, review precision), the controls (controls list, status, plan), the routines (routines templates, list, status), the upgrade check (upgrade check, upgrade maps), the packs kept (pack list, pack show) and report draft;

    • four generic reads (explore models, fields, search, group) that reach any model, an Enterprise app's or a customisation's included, and never a credential.

  • With --allow-writes: 1396 tools, and every write asks you first: the agent host shows you the command line, and nothing runs until you confirm. A host that cannot ask gets a refusal with the command line instead.

  • --confirm-writes=false: the low- and medium-risk writes run without asking. 68 still ask: the high-risk and destructive operations, every change to a bank account (who you pay), and every write of code (a page's or a view's QWeb architecture, which Odoo runs on the server, and the website's custom code, which runs in every visitor's browser). Use it only for an agent that reads no text written outside the company: a lead from a website form or an email, a sign-up or a message can carry instructions, and an agent can follow them.

  • Hosts with a short tool list: above 60 tools the server offers four catalog tools instead: odoo_find, odoo_describe, odoo_read and odoo_write. Use --mode tools to get one tool per operation.

  • How much of your data the agent sees (--data-access): you choose a level, and odoo-cli enforces it in what it answers the agent:

    • act (the default): names, references, states, dates and amounts. Personal data (phones, emails, addresses, tax and identity numbers, bank accounts), free text (notes, descriptions, messages) and files are withheld and marked [withheld: ...]. Free text is also where outsiders' instructions arrive.

    • operate: the agent acts without seeing data. It has no read tool but discovery and resolve, which finds a record's id from its name or state and answers nothing else. A result gives the status, the record id and whether odoo-cli's own check of the result passed.

    • full: everything a read returns.

    profile set --agent-data-access records a profile's level, and a server can lower it, never raise it.

  • A trace of what the agent did. Each call an agent makes is appended to a local journal of the profile, chained by hashes. An entry holds when it ran, which agent host, the command line, whether you confirmed it, and what came of it. It never holds what a read returned. trace list shows it, and trace verify finds an entry removed or changed. ODOO_CLI_TRACE=off turns it off.

  • Bounds, and a stop switch.

    • odoo-cli agents stop --profile my-odoo --reason "..." refuses every call of every agent on that profile, from its next call, and stops routines, until agents resume.

    • --max-calls and --max-writes bound a session.

    • --rate caps calls a minute (60 by default), since each call takes one of Odoo's workers.

  • Judge a write: --accept. It is added to any write and answers:

    • ODOO_VALID: Odoo ran it as Odoo does;

    • INTENT_VERIFIED: every value asked for is what Odoo holds;

    • RESULT_ACCEPTED: no review check finds a problem with the record, for example an invoice made twice or one dated back into a month that had ended.

  • Secrets never pass through the chat. When a task needs a password, an API key or a token, the agent calls credential set --browser or secret env --browser, and you confirm. You then type the value on a one-time page on your own machine (127.0.0.1, one submission, 5 minutes), and the agent gets only "stored". secret env writes only .env files, kept at mode 600. ODOO_CLI_SECRET_INPUT=off turns the page off, and ODOO_CLI_THEME=plain drops its colours.

  • What the agent cannot choose: the profile, accepting unverified results, or the provider context. It writes a file on your machine only through secret env, with a value you type yourself. Installing a module is never offered.

The server acts with the full authority of the profile's Odoo credential. It bounds what is offered; it is not a sandbox. Its instructions tell the agent that text inside records is data, never an instruction; that is advice to the agent, and what holds is what a write needs before it runs. An agent that can run commands can run odoo-cli directly. See docs/odoo-online-and-enterprise.md for how it compares with Odoo's own MCP server.

When odoo-cli gets it wrong: odoo-cli report draft

An agent is told, in the server's instructions and in every internal error, to offer you a report when odoo-cli itself fails it: an internal error, a result that contradicts the command's description, misleading help, or a gap it had to work around. It runs odoo-cli report draft, which builds the report on your machine and sends nothing. Endpoints, databases, logins and profile names from your profile file, URLs, e-mail addresses, IP addresses, phone-like numbers, long tokens and password=-style values are scrubbed first, and command flags and values are dropped. The agent shows you the draft and asks. If you agree, it gives you a link that opens a prefilled issue in this repository, and GitHub shows you the report again before you press Submit. The fingerprint in the title finds earlier reports of the same fault, so you can add to one instead.

odoo-cli report draft --kind WORKAROUND_USED --command "inventory transfer list" \
  --summary "no scheduled_date in the list" --workaround "read stock.picking with explore" --output json

Review the books: odoo-cli review

Twenty checks find mistakes, duplicates and signs of fraud in a period of your Odoo's books, and the period's figures month by month:

odoo-cli review run --profile my-odoo --from 2026-07-01 --to 2026-09-30 --markdown-file q3.md --output json
odoo-cli review checks --output json      # each check: question, formula, thresholds, how to read it
  • Checks are files. A check can be a declared file with its own cases. Add your own with review add-check. Say which findings were real with review verdict, and read each check's share of real findings with review precision. See docs/checks.md.

  • What the checks look for:

    • bills, customer invoices or payments entered twice, duplicate partners;

    • a supplier paid into an employee's account, a new supplier paid fast;

    • orders split under the approval threshold, a payment cut into pieces under it;

    • sales below cost, a tax rate unlike the product's;

    • refunds after payment;

    • documents dated back into a month that had ended, revenue booked at a month's end and credited early in the next;

    • in Vietnam, purchases of 5 million đồng or more paid in cash, as one bill or as several bills of one seller on one day: they lose the input VAT deduction (Nghị định 181/2025/NĐ-CP, Điều 26);

    • expenses claimed twice, on leave or in round amounts;

    • valuable scrap;

    • overdue receivables.

  • What each finding carries: its formula, its thresholds (--param O01.window_days=10) and the records that are its evidence.

  • How to read a finding: it says cần xem lại, "look at again", and is never a verdict.

  • A check that cannot read (a module not installed, a model the series removed, a field the read does not return) is listed as not run, with the reason, never as clean.

  • Proof: the checks were proven on a simulated business with 24 planted cases, all found, and 11 look-alikes, none flagged. Agents get them as MCP tools, and the reading notes come with them.

Turn on Odoo's own controls: odoo-cli controls

The review finds problems after they happened. Most of them have a switch in Odoo that would have stopped them. odoo-cli controls says which, reads whether each is on, and turns the reversible ones on through verified operations. Nothing is added to Odoo.

odoo-cli controls list --output json                       # what each does, where Odoo enforces it, whether it can be undone
odoo-cli controls status --profile my-odoo --output json   # ON, OFF, PARTIAL or NOT_AVAILABLE
odoo-cli controls plan --profile my-odoo --enable K01,K04 \
    --param K01.amount=20000000 --param K04.date=2026-09-30 --output json
odoo-cli controls apply --profile my-odoo --enable K01,K04 \
    --param K01.amount=20000000 --param K04.date=2026-09-30 --plan-hash sha256:... --output json

Control

What Odoo does once it is on

Where Odoo enforces it

K01 Purchase approval from an amount

an order at or over the amount waits for a Purchase Administrator

server

K02 Lock approved purchase orders

read-only in the web client; cannot be cancelled while locked

web client

K03 Lock confirmed sales orders

the same, for sales orders

web client

K04 Lock the books up to a date

entries of the locked period cannot change; a late document is posted after the lock date. Community has no screen for lock dates

server

K05 Restrictive audit trail

posted entries, their attachments and their change log cannot be deleted

server

K06 Hash posted entries

posted entries can only be reversed. odoo-cli never turns it on: Odoo never lets it be undone, so controls plan says how to do it in Odoo

server

K07 Customer credit limit

a warning on quotations and draft invoices

warning only

K08 Two-step sign-in for employees

a code by email at sign-in; Odoo then refuses employees' passwords over the API, so integrations need API keys

server

K09 Alert: a bank account added or changed

an activity for the person you name, on that account

notify

K10 Alert: a posted entry reset to draft

an activity on that entry; posting it again adds none

notify

K11 Alert: a trading partner's tax number or name changed

an activity on that partner; a partner that never traded is not reported

notify

  • Odoo's limits are said plainly: "web client" means a caller of the API is not stopped, and K07 only warns.

  • Alerts (K09-K11) are Odoo automation rules from the base_automation module, which you install.

    • Their only action schedules an activity for the person you name: --param K09.user="Ngọc Lan", or their id.

    • odoo-cli's alert operations reach only the rules odoo-cli made.

    • They never offer an action that runs code, writes records, sends a message or calls a webhook.

    • There is no alert for a changed lock date: Odoo cannot schedule an activity on a company. The change history shows it.

  • Safe by construction:

    • apply only turns controls on, and refuses a plan that changed since you reviewed it (--plan-hash);

    • it never moves a lock date back, never sets the hard lock date, and never weakens a stronger setting already on;

    • an agent can list, read and plan; applying stays with you.

  • The review points at them: a finding of O16 or O26 names the control that would have prevented it (review checks lists them as preventedBy).

  • Proof: on Odoo 19.0 and 20.0 labs, each control was turned on through odoo-cli, and what Odoo then does was exercised: an order over the amount waited for approval, an entry of a locked period could not be reset to draft, a posted bill could not be deleted, a password was refused over RPC. Then everything was put back (conformance/odoo-controls-conformance.py). Saving the Settings page is now verified setting by setting on both series.

Routines: odoo-cli routines

The questions a business asks its Odoo every morning, every Monday or on the first of the month, run on a timer and land in a folder an agent reads.

odoo-cli routines templates --output json                       # ten starter routines: what each asks, what Odoo returns
odoo-cli routines init --profile my-odoo --agent claude         # writes routines.toml, yours to change
odoo-cli routines run --routine cong-no-qua-han                 # one now
odoo-cli routines install                                       # "routines due" every 15 minutes (systemd, launchd, Task Scheduler or cron)
odoo-cli routines status --output json                          # each routine's latest run
  • What a run leaves: each run is kept in ~/asao-routines/<routine>/<day>/:

    • result.json: exactly what Odoo answered;

    • summary.md: counts and totals, computed and never written by a model;

    • analysis.md: what the agent made of it. The agent can be Claude Code, Codex, Gemini CLI, agy, any command, or none.

  • For an interactive agent: AGENTS.md, CLAUDE.md and GEMINI.md in that folder tell it how to read the runs.

  • Reads stay reads:

    • a read or group step can only run a read operation;

    • a routine changes Odoo only through steps that say so (writes = true), and never installs a module;

    • the agent only reads.

  • A missed slot:

    • a laptop that was off at 07:30 runs the daily routine when it wakes;

    • a run that could not reach Odoo leaves the slot open for the next check.

  • Same file as the SDK: the format is the one the asao-erp SDK's routines use (TOML, or YAML and JSON). Credentials stay in the keyring or the credential file; the timer keeps only paths.

  • Timers: tried on systemd (Linux). The launchd (macOS), Task Scheduler (Windows) and cron installers are written but not tried yet; routines install --print shows what each would set up.

A team on one Odoo: odoo-cli spec --backend

odoo-cli spec plan  --profile company --file company.yaml --backend s3://my-bucket/odoo/company
odoo-cli spec apply --profile company --file company.yaml --backend s3://my-bucket/odoo/company --plan-hash sha256:...
odoo-cli spec state --backend s3://my-bucket/odoo/company
  • The backend holds the lock (one apply at a time), the plans saved for review, a record of every apply, and the spec last applied in full, which a teammate pulls back with spec pull.

  • Where it lives: an S3 bucket, Cloudflare R2, MinIO, Backblaze B2 or a shared folder. odoo-cli signs S3 requests itself.

  • No state file: every plan reads Odoo itself. With a backend, apply needs the reviewed plan's hash.

  • Who approves: the backend records who as a label. For an approval by a checked identity, use your git host's reviews or ASAO; docs/team-spec.md has both, with GitHub Actions workflows.

Before an upgrade: odoo-cli upgrade check

odoo-cli upgrade check --profile my-odoo --to 20.0 --output json
odoo-cli upgrade check --profile my-odoo --to 20.0 --markdown-file upgrade-20.md   # a report in Vietnamese
odoo-cli upgrade maps --output json                                                 # the pairs it can check

It reads what people made in the database, the records no module owns:

  • saved filters, automation rules and server actions;

  • custom fields, email templates and views;

  • record rules, access rights, export templates, default values and window actions.

It then checks every model and field they reference against the next series. Each finding names the customization, the path that stops resolving, and why:

  • BREAKS: it stops working. RENAME: it stops working too, and the finding names the field's probable new name.

  • CHECK: it still runs but may mean something else. For example, a filter on a value that 20.0 dropped then matches nothing.

  • UNKNOWN: it depends on a module the map does not cover, such as an Enterprise or a third-party module.

It reads and never writes, and it never prints the code or the templates it reads. Run it as an administrator: only an administrator can read the customizations.

What 20.0 changes. Record rules and access rights move into one new model, ir.access, so every custom rule has to be written again. 44 models are gone, among them time-off types, banks and scrap orders, and so are 618 fields. 56 selection fields lose values.

How far to trust it. The map comes from the conformance labs of both series. It was measured by planting customizations on 19.0 and making the same ones again on 20.0:

  • Research set: before the check was tuned on it, it flagged 8 of the 10 that broke, with one false alarm among the 8 that still worked. Those three faults were fixed.

  • Fresh set: 23 customizations written after the check was finished, never used to change it. The upgrade-check workflow measures it in the open (run 38968869331). 20.0 refused 13 of them, and the check flagged all 13. Among the 10 that still work it gave no false alarm, and one CHECK: a saved filter on a value that 20.0 dropped. It found nothing on the 56 customizations the lab makes itself.

The people who wrote these sets knew what 20.0 changes. So the sets show that the map holds on a real 20.0. They do not show how many kinds of customization in the wild it can read. It does not read report templates, website pages, or code that builds names at run time. Read every finding, and test the upgrade on a copy of the database.

Your own modules: operation packs

A custom module can bring its own operations to odoo-cli as a pack: a JSON file the module ships as static/odoo-cli/pack.json, which names its reads, writes, buttons, wizards and reports. It holds data, never code.

odoo-cli pack sync --profile my-odoo --dry-run    # the packs your custom modules ship
odoo-cli pack sync --profile my-odoo              # check them against your Odoo and keep them
odoo-cli pack show --profile my-odoo --module my_module
odoo-cli pack generate --profile lab --module my_module --file pack.json   # for the module's author
  • Checked against your Odoo. odoo-cli checks a pack before it keeps it: every model and field exists, and every method is a button a person can click in the model's views.

  • Refused outright. A pack can never name users, rights, settings, code, mail or a credential, never delete, and never call a private or ORM method.

  • Unverified, and agents ask first. A pack's operations need --allow-unverified. On mcp serve, its writes always ask you first, even with --confirm-writes=false.

  • More: the guide is docs/packs.md. The proof is conformance/packs/prove.sh: a demo module whose pack runs through odoo-cli, checked against Odoo.

Any model: odoo-cli explore

The catalog covers the business apps of Odoo Community. To read anything else, such as an Enterprise app, a third-party module or a field added in Studio, use the generic reads. They are what Odoo 20 Enterprise's own MCP server offers, and they also run on Community:

odoo-cli explore models --profile my-odoo --search planning --output json
odoo-cli explore fields --profile my-odoo --model planning.slot --output json
odoo-cli explore search --profile my-odoo --model planning.slot --filter '[["state","=","published"]]' --limit 5 --output json
odoo-cli explore group  --profile my-odoo --model helpdesk.ticket --group-by stage_id --output json

They read and never write. Their answers are Odoo's own and are not checked by conformance, so each one says "generic": true. They never show:

  • models that hold credentials (system parameters, mail servers, payment providers, API keys, certificates, bank links);

  • fields whose names mark a secret, whether as a value, a filter or a group;

  • binary fields.

Before it acts

Before running an operation, odoo-cli checks the Odoo it reaches.

It finds

It does

The endpoint does not answer as Odoo

Refuses (AERP_PROVIDER_NOT_ODOO). Nothing is sent, not even the credential

A series it does not support

Refuses, and says why: older than 19.0, newer than it knows, or an intermediate Odoo Online release (saas~)

20.0

Runs only with --allow-unverified. A translation layer maps 19.0's names onto 20.0's; 1170 of the 1291 operations 20.0 can run are verified (62 cannot exist there: their models, dialogs or buttons are gone)

The operation's modules are not installed

Refuses before sending (AERP_MODULE_NOT_INSTALLED)

A write where this Odoo is customised: a model that Enterprise or third-party modules extend, an automation watching it, or a required field added by hand

Holds it back (AERP_ENVIRONMENT_UNVERIFIED) until you pass --allow-unverified

Reads on a customised Odoo

Runs them, with a note

Customised Odoo is not guaranteed. The operations are verified on the stock Odoo image of each series only. Some Odoo instances are changed beyond that image, by Enterprise, third-party or in-house modules, Studio, automations or fields added by hand. On such an Odoo, operations cannot be verified, and nothing guarantees that they work as described or without error. An operation you accept there runs at your own risk. odoo-cli doctor --probe shows what it found. See docs/provider-fit.md.

Other guarantees:

  • Credentials: no command prints credential material, and it is redacted from errors and data.

  • Deletes: an operation that would affect dependent records needs --confirm-impact.

  • Errors: the envelope carries a stable code, the reason, a remediation, and the structured context a caller needs to correct itself. Exit codes separate configuration, denial, conflict and provider failures.

  • Trust boundary: odoo-cli acts with the full authority of the Odoo credential in the profile. It is for a single trusted operator: a developer, a local automation, or an agent running under your own identity. Run odoo-cli trust --output json for the full statement.

Python

The asao-erp package drives odoo-cli from Python. It runs every call through odoo-cli, so the same checks, refusals and data levels apply as on the command line, and it holds no credential of its own.

pip install asao-erp
from asao_erp import Client, ops, to_frame

erp = Client(profile="my-odoo")
# Odoo sums the month itself: one request and twelve rows, not thousands of lines pulled to be summed.
sales = erp.group(ops.sales.quotation_list, by=["date_order:month"], aggregate=["amount_total:sum"],
                  filter=[["state", "=", "sale"]])
to_frame(sales)

report = erp.review("2026-07-01", "2026-09-30")          # odoo-cli review run, computed
to_frame(report["findings"])                             # one row per finding, ready to sort and chart

When to use which:

Use

When

odoo-cli

one action, a shell script, a schedule (routines), an agent over MCP

asao-erp

analysis: many reads put together, pandas, a notebook, a monthly report

group() asks Odoo for the totals instead of pulling every record. This is lighter on Odoo, whose workers an agent shares with the people using it.

Not yet

  • Odoo Enterprise and Odoo Online are not verified. On 2026-10-14 odoo-cli reached an Enterprise 20.0 trial on Odoo Online: none of its 580 reads disagreed with Odoo, 415 of 553 writes and 75 of 192 buttons and dialogs agreed, and one button behaved differently on Enterprise. Odoo Online allows the external API on its Custom plan only. See docs/odoo-online-and-enterprise.md, which also compares odoo-cli with Odoo's own MCP server.

  • Business actions left out on purpose:

    • Sending email or SMS: a quotation, an RFQ or an invoice (odoo-cli prints the PDF instead), a mailing (launching, scheduling, testing it), invitations to a course or an event, coupons, abandoned-cart reminders, survey reminders, lunch notifications.

    • Creating a user or a company from another record.

    • Paid IAP services (lead generation, enrichment, SMS credits).

  • Not verified yet: 67 operations, most of them writes and actions whose records the lab's data cannot supply, or whose business rules refuse generic values.

  • Self-update: odoo-cli update needs a signed release channel that is not published yet. To update, run the installer again.

This repository

This repository is exported from the ASAO monorepo; SOURCE.md names the commit.

  • The root is the part of the command-line module that odoo-cli links.

  • third_party/ holds the Odoo core (packages/odoo, with its tests) and the endpoint policy.

  • conformance/ holds the tools that verify the catalog against a live Odoo.

  • docs/ holds the catalog's research and evidence.

Issues and pull requests are welcome. An accepted change is applied in the monorepo and arrives with the next export.

make build   # bin/odoo-cli
make test    # the command line and the Odoo core

Support the project

odoo-cli is free and built by one person. If it saves you time and you would like to chip in, any amount helps:

PayPal

Vietcombank (VietQR, any Vietnamese banking app)

paypal.me/AldenNguyen2345

NGUYEN HUU HOANG · 1031712729

A star, an issue or a note about what you use it for helps just as much. Support buys no priority and no warranty; the license and its terms stay the same.

License, owner, privacy and terms

  • License: Apache License 2.0, see LICENSE and NOTICE. The software is provided "as is", without warranty (sections 7 and 8 of the license).

  • Owner: Copyright 2026 Nguyễn Hữu Hoàng, who publishes it under the brand ASAO. Contact: nguyenhuuhoang711@gmail.com.

  • Privacy: odoo-cli collects nothing. It has no telemetry and no account, and it connects only to the Odoo you configure; odoo-cli update, when you run it, also reaches the release channel you name. Your Odoo credential stays on your machine: in the OS keyring, a file you name, or an environment variable. What it reads from your Odoo goes to your terminal, your files or the agent you connect, never to ASAO.

  • Terms: using this software on its own is governed by its license only. ASAO's hosted service (asao.vn) has its own Terms of Service and Privacy Policy, which apply when you use that service, including through the asao CLI.

Tiếng Việt

odoo-cli là công cụ dòng lệnh cho Odoo Community. Nó có 1368 thao tác nghiệp vụ, trong đó 1301 thao tác đã được kiểm chứng trên bản Odoo 19.0 chuẩn: đọc, ghi, bấm nút nghiệp vụ, hộp thoại, in PDF và xoá có kiểm soát. Mọi lệnh trả về JSON, dùng được cho script, notebook và AI agent. Trên Odoo 20.0, 1187 trong 1306 thao tác áp dụng được đã được kiểm chứng.

Tự kiểm lại được. Workflow conformance chạy công khai mỗi tuần và mỗi lần phát hành:

  • dựng Odoo 19.0 và 20.0 từ image chính hãng (ghim theo digest);

  • chạy mọi thao tác qua odoo-cli;

  • đối chiếu từng kết quả với chính Odoo qua một bộ đọc JSON-RPC độc lập.

Workflow báo lỗi khi một thao tác đã kiểm chứng nay lệch với Odoo. Trên máy có Docker hoặc Podman, bạn chạy conformance/odoo-conformance-lab.sh --series 20.0 --out /tmp/conformance-20.

docs/conformance.md (có phần tiếng Việt) giải thích:

  • "khớp" nghĩa là gì;

  • cách đọc báo cáo;

  • những gì việc kiểm chứng này không chứng minh.

Trước mỗi thao tác, CLI kiểm tra Odoo mà nó kết nối:

  • có phải Odoo không, và phiên bản nào;

  • thiếu module gì;

  • có bị tuỳ biến không.

Gói Python đi kèm: pip install asao-erp dùng cho phân tích: ghép nhiều lần đọc, đưa vào pandas, làm báo cáo tháng, rà soát ra bảng. Gói chạy mọi lệnh qua odoo-cli, nên mọi kiểm tra và giới hạn vẫn giữ nguyên. erp.group(...) để Odoo tự tính tổng thay vì kéo hết bản ghi về, nên nhẹ cho Odoo hơn. Ví dụ ở mục Python.

AI agent qua MCP: odoo-cli mcp serve --profile my-odoo đưa catalog cho agent qua MCP, chạy được cả trên Community lẫn Enterprise.

  • Mặc định chỉ đọc: 623 công cụ, gồm 4 công cụ explore đọc được mọi model (cả app Enterprise và trường tự thêm), không bao giờ đọc mật khẩu hay khoá bí mật.

  • Thêm --allow-writes: agent được ghi, và thao tác ghi nào cũng hỏi bạn trước: agent host hiện dòng lệnh, bạn xác nhận thì mới chạy. Agent host nào không hỏi được thì nhận lời từ chối, kèm dòng lệnh để bạn tự chạy.

  • Agent được xem bao nhiêu dữ liệu (--data-access): bạn chọn cấp, odoo-cli giữ đúng cấp đó trong mọi câu trả lời gửi cho agent.

    • act (mặc định): agent thấy tên, mã, trạng thái, ngày và số tiền. Thông tin cá nhân (SĐT, email, địa chỉ, mã số thuế, CCCD, tài khoản ngân hàng), chữ tự do (ghi chú, mô tả, tin nhắn) và file đều bị giấu.

    • operate: agent chỉ làm mà không xem dữ liệu. Kết quả chỉ có trạng thái, id bản ghi và việc odoo-cli tự kiểm kết quả có đúng không.

    • full: agent thấy đầy đủ.

    Đặt cấp cho profile bằng profile set --agent-data-access. Khi chạy server chỉ được hạ cấp, không được nâng.

  • Dấu vết việc agent đã làm: mỗi lệnh agent gọi được ghi vào nhật ký trên máy, nối chuỗi bằng mã băm. Nhật ký ghi: lúc nào, agent nào, dòng lệnh, bạn có đồng ý không, kết quả ra sao. Nhật ký không ghi dữ liệu đã đọc.

    • trace list để xem;

    • trace verify để phát hiện dòng nào bị xoá hay bị sửa.

  • Nút dừng và giới hạn:

    • agents stop --profile my-odoo dừng mọi agent và lịch tự động trên profile đó ngay ở lệnh kế tiếp;

    • --max-calls và --max-writes giới hạn một phiên;

    • --rate (mặc định 60 lệnh mỗi phút) để agent không làm nghẽn Odoo.

  • Chấm kết quả một lệnh ghi (--accept): Odoo chạy đúng chuẩn, bản ghi đúng như yêu cầu, và không phép kiểm rà soát nào thấy vấn đề, như hoá đơn trùng hay ghi lùi ngày.

  • Bí mật không bao giờ đi qua chat: khi cần mật khẩu, API key hay token, agent gọi credential set --browser hoặc secret env --browser, bạn xác nhận, rồi tự gõ vào một trang dùng một lần trên chính máy mình. Agent chỉ nhận "đã lưu". Muốn tắt trang thì đặt ODOO_CLI_SECRET_INPUT=off; muốn giao diện trơn thì đặt ODOO_CLI_THEME=plain.

  • --confirm-writes=false: các thao tác ghi rủi ro thấp và vừa chạy không cần hỏi. 61 thao tác rủi ro cao hoặc xoá, và mọi thay đổi tài khoản ngân hàng (người nhận tiền), vẫn hỏi. Chỉ dùng khi agent không đọc văn bản do người ngoài công ty viết: lead từ form website hay email, người đăng ký, tin nhắn đều có thể chứa lệnh, và agent có thể làm theo.

  • Agent không tự chọn được: profile, việc chấp nhận kết quả chưa kiểm chứng, hay việc ghi file lên máy bạn.

Chốt kiểm soát của chính Odoo: odoo-cli controls cho biết công tắc nào trong Odoo chặn trước được việc phép kiểm tìm ra: duyệt đơn mua từ một mức tiền, khoá sổ đến một ngày, không cho xoá dấu vết kế toán, hạn mức nợ của khách, xác thực hai bước cho nhân viên... Nó đọc xem công tắc nào đang bật, và bật những cái tắt lại được qua thao tác đã kiểm chứng, có kế hoạch để bạn xem trước.

  • Odoo Community không có màn hình đặt ngày khoá sổ; odoo-cli đặt được, và không bao giờ lùi ngày khoá.

  • Niêm phong bút toán bằng mã băm thì odoo-cli không bật, vì Odoo không cho tắt lại.

Việc định kỳ: odoo-cli routines chạy những câu hỏi doanh nghiệp hỏi Odoo mỗi sáng, mỗi thứ Hai hay đầu tháng (bán hàng hôm qua, công nợ quá hạn, phiếu kho trễ, rà soát sổ sách tháng trước), theo lịch.

  • Mỗi lần chạy được lưu thành thư mục: số liệu Odoo trả về, bản tóm tắt do máy tính, và phần đọc của agent bạn chọn (Claude Code, Codex, Gemini CLI, agy hoặc không dùng agent).

  • Không cần Python. Lệnh routines install đặt lịch chạy tự động trên máy.

Làm theo nhóm trên một Odoo: odoo-cli spec --backend s3://.... Bucket S3, R2, MinIO hay một thư mục dùng chung giữ khoá (mỗi lúc chỉ một lần apply), các plan đã lưu để xem lại, lịch sử mọi lần apply, và spec đã apply gần nhất để kéo về. Không có tệp state: mỗi lần plan đều đọc thẳng Odoo. Xem docs/team-spec.md.

Trước khi nâng cấp: odoo-cli upgrade check --to 20.0 đọc những gì người dùng tự tạo trong cơ sở dữ liệu:

  • bộ lọc, quy tắc tự động, hành động máy chủ;

  • field tự tạo, mẫu email, giao diện;

  • quy tắc bản ghi, quyền truy cập, mẫu xuất, giá trị mặc định.

Nó cho biết cái nào sẽ hỏng ở bản mới, vì sao, và field có lẽ đã đổi tên thành gì. Nó chỉ đọc, không bao giờ in code hay nội dung mẫu. Ở bản 20.0, quy tắc bản ghi và quyền truy cập chuyển sang một model mới (ir.access), nên mọi quy tắc tự tạo phải viết lại. Thêm --markdown-file để có báo cáo tiếng Việt. Đo trên Odoo thật với 23 tùy biến viết sau khi làm xong bản kiểm tra: 20.0 từ chối 13 cái, bản kiểm tra báo đủ cả 13; trong 10 cái vẫn chạy, nó không báo nhầm cái nào. Đây vẫn là dấu hiệu cần xem lại, không phải phán quyết: hãy thử nâng cấp trên một bản sao trước.

Module tự viết: module custom có thể kèm một gói thao tác (static/odoo-cli/pack.json). Gói khai báo các thao tác đọc, ghi, nút bấm, wizard và mẫu in của module, chỉ là dữ liệu, không phải code. Hai lệnh dùng:

  • odoo-cli pack sync tìm gói của các module đã cài, kiểm với Odoo của bạn rồi mới giữ lại: model và field phải có thật, method phải là nút mà người dùng bấm được trên màn hình;

  • odoo-cli pack generate viết sẵn gói cho người viết module.

Gói không bao giờ được đụng tới người dùng, phân quyền, cài đặt, code, mail hay bí mật, và không được xóa. Thao tác của gói phải thêm --allow-unverified. Agent ghi gì qua gói cũng phải hỏi bạn trước. Hướng dẫn: docs/packs.md.

Odoo Online và Enterprise: odoo-cli kết nối được qua API JSON-2 (cần gói Custom của Odoo). Ngày 14/10/2026, trên một bản dùng thử Enterprise 20.0: 580 thao tác đọc không lệch thao tác nào; 415/553 thao tác ghi và 75/192 nút nghiệp vụ khớp với Odoo; một nút chạy khác trên Enterprise. Chi tiết: docs/odoo-online-and-enterprise.md.

ASAO chỉ kiểm chứng trên bản Odoo chuẩn. Với Odoo đã tuỳ biến, ASAO không thể kiểm chứng và không bảo đảm thao tác chạy đúng; chạy trên Odoo đó là do người dùng tự chịu rủi ro. Chi tiết: docs/vi/pham-vi-kiem-chung-va-bao-dam.md.

Giấy phép, chủ sở hữu, quyền riêng tư. odoo-cli phát hành theo giấy phép Apache 2.0, thuộc sở hữu của cá nhân Nguyễn Hữu Hoàng (thương hiệu ASAO), và được cung cấp "nguyên trạng", không kèm bảo đảm. odoo-cli không thu thập dữ liệu gì: không có telemetry, không cần tài khoản, chỉ kết nối tới Odoo bạn cấu hình. Thông tin đăng nhập Odoo nằm trên máy của bạn, và dữ liệu đọc được không gửi về ASAO. Dịch vụ ASAO (asao.vn) có Điều khoản sử dụng và Chính sách quyền riêng tư riêng; chúng chỉ áp dụng khi bạn dùng dịch vụ đó.

Ủng hộ dự án. odoo-cli miễn phí và do một người làm. Nếu nó giúp bạn tiết kiệm thời gian và bạn muốn ủng hộ một chút: chuyển khoản Vietcombank 1031712729 (NGUYEN HUU HOANG), quét VietQR bằng app ngân hàng nào cũng được, hoặc PayPal paypal.me/AldenNguyen2345, hoặc thẻ quốc tế qua ko-fi.com/nguyenhuuhoang. Mã QR ở mục Support the project. Một sao trên GitHub, một issue hay một lời kể bạn dùng nó vào việc gì cũng quý như vậy.

Related MCP Connectors

Related MCP Servers

  • A
    license
    D
    quality
    D
    maintenance
    MCP server for Odoo 18 that provides 85 tools for CRUD operations, business actions, and migration workflows, enabling natural language interaction with Odoo ERP.
    85
    5
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A read-only MCP server that enables AI assistants to query Odoo instances via XML-RPC, supporting search, read, count, and field inspection without requiring custom modules.
    68 PyPI
    1
    Mozilla Public 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables AI assistants to safely query live Odoo 19 data with a defense-in-depth security layer, providing read-only tools for partners, sales, inventory, and finance.
    MIT