Skip to content

Security & Multi-Company

Who this is for

The operator setting up users, and anyone verifying that the data-separation story actually holds up (e.g. before an audit). Day-to-day users usually don't need this page.

What you'll accomplish: understand who can see what, verify the separation works, and fix access-control problems when they surface.


The business rule in plain English

Two companies share one Odoo database:

  • Reyder Enterprises — runs the operation, owns customer relationships, and handles Reyder sales orders and customer invoices. A Reyder-only user sees Reyder records plus consigned devices that an active agreement permits—not every record in both companies.
  • Axis Mobile — owns the devices, receives settlement payments. Should see only their own devices and their own owner-settlement reports. Must not see Reyder's customer names, sale prices, SO numbers, or internal manifests.

This is not a “please don't look” convention. For normal users, Odoo record rules control ORM operations used by the UI and API. They are not database row-level security: raw SQL and Odoo SUPERUSER execution can bypass ORM controls, so those capabilities are restricted to trusted system administrators and sit outside the operator-access boundary.


At-a-glance comparison

An Axis user sees A Reyder user sees
Their devices (IMEI, model, grade, status) Reyder devices + permitted Axis devices (via consignment)
Axis owner settlement reports only Reyder consignee settlement reports only
Their consignment agreements Their consignment agreements
Reserved state on their devices (via stock.lot) — not the reservation record itself Full retail reservations (since Reyder is the holding company)
Device traceability (where Axis's devices are) Full warehouse operations
no customer names All customer data
no sale prices All pricing
no SO numbers All SOs
no Reyder manifests All manifests

Set up users the right way

Assign people to the correct company

A user who works for Axis should belong only to Axis. A user who works for Reyder should belong only to Reyder. Only system administrators should belong to both.

Open Settings → Users & Companies → Users and edit the user:

User groups assignment

  • Companies — tick only the companies this user works for
  • Default Company — what they log into

If someone legitimately works for both, they belong to both — and the system will auto-expand their company context (see the gotcha below). But defaulting everyone to both-companies breaks the separation test.

Test profiles

Account type Belongs to Purpose
Dual-company administrator Both companies Administration and authorized cross-company investigation
Admin-provisioned Axis test user Axis Mobile only Verify that an Axis-only user cannot see Reyder data

Always test with a real single-company profile

A dual-company administrator is not a valid separation test. Ask an administrator to provision an approved Axis-only test account, keep its password in the approved credential system, and never publish or share that password in a guide.


What enforces the separation

For normal user access, the security boundary combines record rules with model-level data minimization. One-way links and invisible form fields further reduce accidental exposure, but they are not independent API/export boundaries and do not replace the first two controls. Raw SQL and SUPERUSER access remain administrator-only and outside this model.

Layer 1 — Record rules (the primary enforcement)

Odoo record rules filter ORM reads and writes made through the normal UI and API. The module adds these rules on top of the stock defaults:

Model Domain Effect
Settlement report company_id in company_ids Axis sees only their owner reports; Reyder sees only their consignee reports
Settlement report line Follows parent report Lines are only visible if you can see the report
Device manifest company_id in company_ids Each company sees only their manifests
Consignment agreement owner in company_ids OR consignee in company_ids Both parties to an agreement can see it
stock.lot (device) Custom — see below Consignee can see owner's devices when an agreement is active
Transfer order source OR destination in company_ids Both parties see it
Retail sale company_id in company_ids Company-scoped
Retail reservation holding_company_id in company_ids Holding company only — owners do not see the reservation record directly; they see reserved state on stock.lot instead
SO line device allocation SO company is allowed and the device is owned by an allowed company or visible to an authorized consignee Requires both the allowed SO company and an allowed owner/consignee relationship

Layer 2 — Sensitive sale detail is absent from owner report lines

When the system creates an owner settlement report, it deliberately omits customer, sale price, sale date, sale-order reference, and consignee commission from the owner line. The owner gets the IMEI, device description, and amount owed—what is needed to reconcile payment without exposing Reyder's commercial detail.

The paired_report_id field points only consignee → owner. The owner report has no reverse navigation link to the consignee report. This reduces normal navigation and accidental disclosure; record rules and data minimization remain the actual access boundary.

Layer 4 — View-level hiding for usability and defense in depth

On the settlement report form, sale_order_id is wrapped in invisible="report_type == 'owner'", so it is not rendered in the owner-side UI. This keeps the form clear and reduces accidental exposure, but invisibility alone is never an API or export security boundary.


The device visibility rule (stock.lot)

The module replaces Odoo's default stock.lot record rule with a custom one that handles consignment:

A user can see a device if any of these is true: 1. company_id is in their company list (the device belongs to them), OR 2. company_id is False (unassigned), OR 3. owner_company_id is in their company list (they own it — Axis sees Axis's devices), OR 4. consignee_company_ids contains one of their companies (they're an authorized consignee — Reyder sees Axis's devices that have an active Reyder-Axis agreement)

Rule 4 is the consignment magic. It's what makes Axis's inventory visible to Reyder the moment an agreement is activated, and invisible again the moment it's terminated.

See Consignment Agreements for the operator workflow.


Verifying the separation (manual test)

Want to confirm it works? Run this quick test:

  1. Log out.
  2. Log in with an administrator-provisioned Axis-only test account using your approved internal credential process.
  3. Try to open Device Ops → Fulfillment → Customer Manifests. You should see zero results for Reyder deliveries.
  4. Open Device Ops → Consignment → Settlement Reports. You should see only SETTLE/OWN/* rows, never SETTLE/CON/*.
  5. Click into an owner report. There's no Customer field, no Sale Price, no Sale Order number.

If any of those tests show Reyder data, stop and escalate it as an access-control incident. The most common cause is that the test account was accidentally allowed into Reyder too.


Common problems

Axis user is seeing Reyder data

Check Settings → Users and open the Axis user. Confirm Companies lists only Axis Mobile. If Reyder is in there too, remove it. This is the #1 cause of leaks.

A dual-company administrator doesn't see separation

By design, that account is allowed into both companies and is not a valid isolation test. Use an administrator-provisioned Axis-only test account.

Reyder user can't see Axis devices

Confirm the agreement is Active and today's date is inside its Start/End dates. A suspended agreement uses Reactivate, not Activate. A valid agreement refreshes device visibility automatically; if a matching device is still missing, do not edit its company fields—send the agreement and IMEI references to support.

A settlement report is visible to both companies

Stop and escalate it as an access-control incident. Do not edit the report company, delete the report, or change its status. Send support the report reference, active company, user profile, and a screenshot of what was visible.

Cron jobs run as SUPERUSER and might post data that bypasses rules

Correct — SUPERUSER_ID=1 bypasses all record rules. This is intentional for crons and backend processes. The protection is at read time for user sessions. Cron-generated data is written by the system on both sides; it doesn't accidentally leak during a read because the record rule still filters what users see.


Administrator checkpoint

Before onboarding either company, test the same representative records with an Axis-only profile, a Reyder-only profile, and an authorized dual-company administrator. Record the test date and result. Detailed shell procedures and implementation notes belong in the private engineering runbook, not in an operator guide.

Reviewed 2026-07-22 · Device Inventory 19.0.2.101.0 · Use the exact on-screen label shown in bold.