A business that runs on Odoo already keeps its products, stock, orders and contacts there. Getting that onto a public website usually means copying it across on a timer, or writing an integration that knows Odoo's models and field names and has to be kept in step as Odoo changes.

There is another way to divide the work: the website asks for a named operation, and the knowledge of how to carry it out in Odoo lives in one package beside Odoo. The website decides who may run which operation; Odoo decides what the account running it may see and change.

The general approach

  1. Name the operations - "list the shop's products", "add a line to a quotation" - in a package that knows Odoo.
  2. Bind each operation to the website with the inputs a request may supply and the people who may cause it.
  3. Run it as an Odoo account whose rights are set in Odoo, by whoever administers Odoo.
  4. Plan a change before applying it, so a write can be shown and confirmed first.

How lazysite does it

The Odoo extension runs named operations from OOM operation packs. Lazysite names the operation, binds its parameters, renders the report it returns, and presents a plan when the operation changes data. Model names, field names and Odoo's call shapes stay in the pack; lazysite's own code holds none of them.

What the operator sets up

  1. Install OOM - the libodoo-oom-perl package (Perl module Odoo::Ops), version 0.03 or newer. With OOM missing or older, every call is refused with a message naming the package and the version found.
  2. Enable the Odoo extension on the Extensions page. It is installed disabled.
  3. Configure the connection on the same page: the Odoo address (https:// to any host; plain http:// only to the same machine), the database, and the connection user - an Odoo account whose rights are set in Odoo. Lazysite grants it nothing and narrows nothing Odoo allows it.
  4. Set the connection user's API key, issued in Odoo, over the control API or MCP (odoo-secret-set, with the manage_connectors capability). The key is stored alone in its own file and is never returned or logged.
  5. Create a binding for each operation: a handler of type odoo (Run an Odoo operation on the Handlers page), configured with manage_connectors. It names the operation, which request fields map to which parameters, any fixed parameters, who may run it (authenticated, public or scheduled), and how a write is applied.
  6. Allow writes, if the site needs them: odoo_allow_writes is off by default, and while it is off a write operation can produce a plan and never applies one.

The Status button on the Extensions page reports whether OOM is present and whether the connection is configured.

Where the data appears

  • On a page, through front matter: stock: odoo:shop-list in tt_page_var puts the report of a reading binding on the page, with every value HTML-escaped. A page binding never runs an operation that writes.
  • In a region the page fills as it loads, with the shipped browser helper:
<div data-ls-odoo="shop-list" data-ls-odoo-limit="10">
  <template><li><span data-ls-field="name"></span></li></template>
  <p data-ls-empty>Nothing to show yet.</p>
</div>
  • From a form, which runs its binding with the form's fields as the request.
  • On the schedule, and through the control API and MCP (odoo_bindings, odoo_run, odoo_confirm) for signed-in callers.

Writes are planned, then applied

With apply: confirm, the default, a write operation returns a plan. Only the account or visitor that made the plan can apply it, with a confirm, within an hour. With apply: immediate, the write applies in the same request. The answer to an applied write lists the records it created and changed, so a page can open the new record straight away.

A binding open to public that writes with apply: immediate lets an anonymous visitor change data in Odoo - adding a line to a basket held in Odoo as a quotation, for example. The visitor carries a server-minted key in a signed cookie, every state-changing request carries a token bound to it, and rate caps apply per visitor and per binding. Writes default to 20 per visitor and 200 per binding each hour; reads to 2,000 and 20,000.

A person's own Odoo account

A binding with credential: session runs as the person signed in to Odoo through the page, with their own Odoo rights, in place of the connection user. A call from someone with no Odoo session is refused and is never run as the connection user. These bindings run from the page's own script, which offers the sign-in (data-ls-odoo-signin). On a site whose people are all Odoo users, odoo_site_signin: on makes the Odoo sign-in the site sign-in, and odoo_group_map turns Odoo groups into site groups.

Every call, refusals included, is one line in a call record that holds fixed fields only - never a parameter value, a report or a key.