Security
What Approval Matrix Pro can read and write, what it stores, and what leaves your instance.
Nothing leaves your instance
No external service, no API key, no outbound HTTP, no telemetry. The module adds no controller and opens no route. Notifications are Odoo's own mail and activities.
The three groups, and why there are three
| Group | Can |
|---|---|
| Approval User | Raise a request; decide where they are named |
| Approval Manager | Design matrices, conditions and levels |
| Approval Administrator | Override a decision, with a recorded reason |
The third split is the one that matters. Designing the rules and forcing a decision past them are different powers. One person holding both quietly removes the control the matrix exists to provide — they can simply approve around it, or edit the rule for one order and edit it back. So Administrator is a separate group, not a superset of Manager, and a deployment where the same person holds both is a deployment that should know it.
Being named as an approver on a level grants nothing on its own. The approver must also be in Approval User, which is what lets them read requests at all.
Conditions cannot execute code
A condition is a field, an operator and a value, evaluated by code in this module. There is no expression field, no safe_eval of user input, and no domain string accepted from the configuration screen.
This is the module's most important security property. An expression box on a business-rules screen is a remote-code-execution hole: anyone who can configure an approval rule could run arbitrary Python as the server user. Dotted field paths cover the real cases without opening that door.
The decision history is append-only
A decision cannot be edited or deleted through the interface. An override is written as its own entry with its reason rather than as an amendment to an earlier one, and no migration rewrites past decisions.
An approval trail that can be rewritten afterwards is not a trail.
Locking is enforced in write, not in the view
When a matrix locks records, the check lives in the model's write. It is not a readonly attribute on a form, because a readonly attribute stops a person using the form and stops nothing else — not the API, not an automated action, not an import.
Fields that are always writable while locked: chatter and activity fields, followers, write_date, write_uid, and the module's own approval fields. Locking discussion makes the workflow unusable without making it safer.
Multi-company
Every model carries a company record rule: matrices, conditions, levels, requests and decisions. A matrix in one company does not match records in another and is not visible from it.
Self-approval
Refused by default. The requester cannot be the approver unless the matrix explicitly allows it, and that allowance is per-matrix so it cannot be switched on globally by accident.
What it does not protect against
- A user with Settings access. Anyone who can edit groups can put themselves in Approval Administrator. This module is a business control, not a defence against a database administrator, and no Odoo module can be the latter.
- Records outside its two models. Only sales orders and customer invoices are governed. Everything else is unaffected.
- Deletion of the module. Uninstalling drops the history with the tables. Export it first if you need to keep it.