# Hosted Community 20: standard UI and invoices

The hosted Camp uses standard Odoo Community 20 modules (`account`, `sale_management`, `calendar`, `l10n_ch`), **one database per workshop, shared by all its teams**. Each team has its own normal user. Customers, orders and invoices are shared; mail/approvals remain team-bound. Different workshop iterations use different databases, without automatically purging earlier runs. No legacy custom Camp/Connector addon is installed. The external adapter provisions standard records and fixed, approval-gated actions without patching Odoo.

`odoo.training.apprunner.ch` reaches the native UI only through the Camp's team-bound browser gateway. Native credentials stay server-side; normal Team users, never administrators, are used. The two-step, browser-cookie-bound handoff rejects stolen/replayed links and ignores client-supplied native session cookies. Database and login switching are blocked. Logging out of the Camp invalidates the gateway session. Native Odoo 20 browser sessions must not send `X-Odoo-Database`: that header deliberately enables stateless requests and disables cookies.

The standard `account` module provides customer invoices without a custom plugin. Operational scheduled/off-site backups are intentionally not enabled. Optional snapshots cover Camp state, the team's own approved order-note changes and its Camp calendar entries, not arbitrary invoice/accounting edits. Shared-order version/receipt chains refuse rollback across another team's or manual edits. Unrelated other-team changes are left untouched. Start a new workshop for a clean shared ERP database; purge remains a separate explicit operation.

The remaining sections document the preserved **legacy local lab**, not the new hosted vanilla databases.

# Legacy local Odoo 20 workshop backend

The full workshop starts with `npm run workshop` on a Linux/WSL host with Docker Compose. Camp listens on http://127.0.0.1:18350; the isolated Odoo gateway listens on http://127.0.0.1:18270. First initialization can take several minutes. Docker Desktop's engine and WSL integration must be working before starting.

## Isolation and synthetic data

Compose project: ai-training-camp-odoo20. Dedicated Postgres/filestore volumes and internal networks share nothing with India or the old Odoo19 lab. Odoo uses the official pinned `odoo:20.0-20261102` image and the trusted provisioning bridge asserts major version 20. Only the Nginx gateway has a loopback host port. The Odoo service has no Internet route, SMTP account or enabled cron workers.

Each Camp workshop gets a separate training_* database. Each team's partners, orders, draft invoices and calendar seed come from the same Faker payload as the Camp mail scenario. Stable x_camp_* identifiers bind the sources. A private manifest under CAMP_DATA_DIR maps teams to policy-gated Connector tokens; never copy it into client configurations or Git.

Only order notes and bounded calendar creation are participant mutations, and only after human approval in Camp. Odoo20 native access domains, field policies and the custom optimistic note guard form server-side checks. In Odoo20, `ir.access` replaces the previous separate ACL/record-rule models; the copied gateway is ported specifically for this teaching instance. Do not install this workshop copy in production. [Official Odoo20 security documentation](https://www.odoo.com/documentation/20.0/developer/reference/backend/security.html)

## Start and connect

```sh
npm ci
npm run workshop
```

Get the trainer token locally with `CAMP_DATA_DIR=var/workshop20 node scripts/access.mjs trainer`. Generate participant configurations with the same data directory and `CAMP_URL=http://127.0.0.1:18350`. Participants connect only to Camp MCP; they do not receive native Odoo credentials. See CLIENTS.md for the isolated Codex/Claude setup.

`bash scripts/odoo-camp-admin.sh ACTION` is a trainer/server-only JSON stdin/stdout bridge for provisioning, checking, logical snapshots, restore and scenario creation. It is not an MCP tool. It refuses non-training database names. Normal source reads and approved writes go through the policy-gated Connector HTTP gateway, not generic XML-RPC/JSON-RPC.

## Two recovery levels

1. Logical team snapshot: Camp data plus agent-writable Odoo order notes and calendar event fields. It does not roll back invoices, payments, accounting configuration or unrelated human changes. Already delivered mail and other teams' calendar copies remain.
2. Full disaster recovery: Camp JSON + private Odoo manifest + every workshop database + filestore. Stop the Camp server cleanly before exporting. This is the backup needed for loss of the workshop host.

```sh
CAMP_DATA_DIR="$PWD/var/workshop20" CAMP_URL=http://127.0.0.1:18350 bash scripts/odoo20-disaster-recovery.sh backup "$PWD/artifacts/dr-backup"
CAMP_DATA_DIR="$PWD/var/workshop20" RECOVERY_OUTPUT_DIR="$PWD/var/recovered-workshop" bash scripts/odoo20-disaster-recovery.sh restore "$PWD/artifacts/dr-backup"
CAMP_DATA_DIR="$PWD/var/recovered-workshop" CAMP_SOURCE_DIR="$PWD/var/workshop20" node scripts/test-recovered-camp.mjs
```

The restore creates fresh training_recovery_* databases and distinct filestore directories; it never overwrites source DBs. The functional proof starts the recovered Camp on 18352, uses authenticated MCP and real Odoo reads/approved writes, and verifies the source Camp file stayed unchanged. Protect all backup files: they contain demo access tokens. Keep a protected off-machine copy; same-disk copies are not disaster protection.

## Verification

```sh
python3 scripts/validate-odoo20-pack.py
python3 test/odoo20-bridge-contract.test.py
CAMP_DATA_DIR=var/acceptance20 node scripts/test-odoo20-live.mjs
```

The first two commands are static/payload checks, not runtime acceptance. The last command actually installs Odoo20 and tests seeded records, approvals, independent denials, logical restore and separate workshop iterations. The GitHub Odoo20 workflow additionally performs combined backup/restore and boots the recovered copy. Actual Codex/Claude model sessions are a separate acceptance condition.

## Legacy lab

The old compose pack and scripts/odoo-new-workshop.sh remain an independent Odoo19 demonstration on 18269. They are not the full current workshop and are not automatically upgraded, stopped or reused by the Odoo20 pack.
