Quick Answer
Odoo RBAC uses 3 layers: Groups (roles), Access Rights (model-level CRUD permissions), and Record Rules (record-level visibility filters) to prevent data disasters. The problem: Warehouse manager accesses customer credit cards, intern deletes $500k orders, sales team sees each other's commissions, accountant modifies tax records = disaster waiting. Real case: $3.6M fashion brand, operations manager accidentally deleted 2,000 confirmed orders (had delete permissions), company spent $47,200 recovering data + re-fulfilling orders, customer churn worse. 73% D2C brands we audit don't have proper RBAC = people have too much access = not malice, negligence. Give someone "admin" to solve one problem, suddenly they see everything. Why it matters: 50 staff with broader access than needed = exponential probability of accidental data loss. Warehouse deletes invoices (clearing clutter), sales reps modify commission (fixing numbers), accountants overwrite taxes (Excel habit), interns access payment data. Single incident cost: $15k-$150k (data recovery, compliance notifications, refunds, legal). Hidden cost: CFOs manually reviewing every transaction = 40 hours/week waste. 3 RBAC layers: (1) Groups (Sales Manager, Sales Rep, Warehouse Staff, Finance) = assign users to groups, each group gets model permissions (quick setup, limited). (2) Record Rules (real control) = sales reps only see own orders [('user_id', '=', user.id)], warehouse only sees own warehouse orders, finance only sees own company invoices = actual data visibility. (3) Field-level access (nuclear option) = hide cost field from sales, hide salary from non-HR, rarely needed but critical. Setup: Define specific roles (not "admin"/"user"), create groups in Odoo (Settings → Users & Groups), define access rights CSV (read/write/create/delete), create record rules XML with domain_force. Access rights CSV: access_order_salesman = sale.order, group_sale_salesman, perm_read=1, perm_write=1, perm_create=1, perm_unlink=0 (never give delete to non-managers). Record rules: domain_force [('user_id', '=', user.id)] = own orders only, [(1, '=', 1)] = see all (managers). Real implementation: Athletic apparel $2.1M/month, 35 staff. Before: operations sees payment data, sales modifies commissions, warehouse deletes records = compliance nightmare. After: 9 specific roles, record rules by user/warehouse/company, hidden sensitive fields = 90% security incident risk drop, compliance met, audit trails clean. Critical: Test by logging in as each role, insurance won't pay breaches without RBAC, humans make mistakes = RBAC prevents mistakes from becoming catastrophes.
The RBAC Disaster Scenario
Your warehouse manager has access to customer credit card data. Your intern can delete $500K worth of orders. Your sales team sees each other's commission rates. Your accountant can modify tax records.
This is a disaster waiting to happen.
We worked with a $3.6M fashion brand last year where the operations manager accidentally deleted a batch of 2,000 confirmed orders because she had delete permissions on the orders model. The company spent $47,200 recovering the data and re-fulfilling orders. The customer churn was even worse.
That's what happens when you don't implement proper access control. Every staff member becomes a loaded gun pointed at your business.
Why RBAC Matters
Access control isn't about trust. It's about math.
If you have 50 staff members and each one has broader access than they need, the probability of accidental data loss goes up exponentially. We see:
• Warehouse staff deleting invoices (thinking they're clearing clutter)
• Sales reps modifying commission rates (trying to "fix" their numbers)
• Accountants overwriting tax records (Excel habit)
• Interns accessing customer payment data (what could go wrong?)
| Cost Type | Amount |
|---|---|
| Single incident cost | $15K-$150K (data recovery, compliance, refunds, legal) |
| Hidden productivity cost | 40 hours/week (CFO manually reviewing transactions) |
| Audit finding | 73% of D2C brands lack proper RBAC |
Proper RBAC fixes this. It doesn't make people feel bad. It makes your system work.
Understanding Odoo's 3 Access Control Layers
Odoo has THREE layers of access control. Most people only use the first one.
| Layer | Purpose | Examples |
|---|---|---|
| Layer 1: Groups | Assign users to roles | "Sales Manager," "Warehouse User," "Finance" |
| Layer 2: Record Rules | Control data visibility | Sales reps only see own orders, warehouse only sees own warehouse |
| Layer 3: Field-Level Access | Hide specific fields | Hide cost from sales team, hide salary from non-HR |
Most D2C brands only use Layer 1. That's why they get hacked, accidentally delete data, and have compliance nightmares.
Setting Up Groups (Layer 1)
First, decide your roles. Be specific.
Don't do "admin" and "user." That's garbage. Do this:
Typical E-Commerce Roles
• Sales Manager: Can see all orders, modify customer info, create invoices
• Sales Rep: Can see only their own orders, limited editing
• Warehouse Manager: Can see all orders + inventory, can ship, cannot delete
• Warehouse Staff: Can see inventory in their warehouse only, can update stock
• Finance Manager: Can see all invoices and payments, can reconcile
• Finance User: Can see invoices only, cannot modify
• Accountant: Can do reconciliation, tax adjustments
• Executive: See everything but can't modify
• Intern: Can see products only, nothing else
Creating Groups in Odoo
1. Go to Settings → Users & Groups → Groups
2. Create a new group for each role
3. Define access rights (to be configured next)
Golden Rule:
Each group should have the minimum permissions needed to do their job. Not more.
Access Rights (Layer 2: Model-Level Permissions)
Access rights answer: "Can this group read/write/create/delete on this model?"
Access Rights CSV Format
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_order_salesman,sale.order salesman,sale.model_sale_order,sales_team.group_sale_salesman,1,1,1,0
access_order_manager,sale.order manager,sale.model_sale_order,sales_team.group_sale_manager,1,1,1,1
access_invoice_finance,account.invoice finance,account.model_account_move,account.group_account_invoice,1,1,0,0
access_invoice_accountant,account.invoice accountant,account.model_account_move,account.group_account_accountant,1,1,1,0
Permission Meanings
| Permission | Meaning | Value |
|---|---|---|
| perm_read | Can view records | 1 = Yes, 0 = No |
| perm_write | Can edit records | 1 = Yes, 0 = No |
| perm_create | Can create new records | 1 = Yes, 0 = No |
| perm_unlink | Can delete records | 1 = Yes, 0 = No (DANGEROUS!) |
⚠️ Critical Rule:
Never give perm_unlink=1 to non-managers. Seriously. This is how data disasters happen.
Record Rules (Layer 3: Record-Level Control)
Access rights are model-level. Record rules are record-level.
| Question | Access Rights Answer | Record Rules Answer |
|---|---|---|
| Can a salesman read orders? | Yes (perm_read=1) | Which orders? Only assigned to them |
This is what actually controls visibility.
Real Example: Sales Team Shouldn't See Each Other's Orders
<!-- File: security/record_rules.xml -->
<record id="sale_order_rule_own_orders" model="ir.rule">
<field name="name">Order - Salesman - Own Orders Only</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="groups" eval="[(4, ref('sales_team.group_sale_salesman'))]"/>
<!-- Only see orders where salesperson = current user -->
<field name="domain_force">[('user_id', '=', user.id)]</field>
<field name="perm_read">1</field>
<field name="perm_write">1</field>
<field name="perm_create">0</field>
<field name="perm_unlink">0</field>
</record>
<!-- Manager sees all orders -->
<record id="sale_order_rule_manager_all" model="ir.rule">
<field name="name">Order - Manager - All Orders</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="groups" eval="[(4, ref('sales_team.group_sale_manager'))]"/>
<!-- No restriction = see all -->
<field name="domain_force">[(1, '=', 1)]</field>
<field name="perm_read">1</field>
<field name="perm_write">1</field>
<field name="perm_create">1</field>
<field name="perm_unlink">1</field>
</record>
Result: Salesman John logs in. Searches for orders. Only sees orders assigned to him. Cannot see Jane's orders. (Yes, this is exactly what competitors are doing to prevent commission disputes.)
Real D2C Implementation: Complete RBAC Setup
Company: Athletic apparel brand, $2.1M/month, 35 staff
Problem: The operations manager could see customer payment data (why?). Sales reps could modify their own commission rates. Warehouse staff could delete order records. Compliance nightmare.
Step 1: Define Roles
1. CEO/Founder (access=everything)
2. Operations Manager (can see orders, inventory, modify statuses)
3. Sales Manager (can see all orders, modify customer info)
4. Sales Rep (can see own orders only)
5. Warehouse Manager (can see all inventory, confirm shipments)
6. Warehouse Staff (can see own warehouse inventory only)
7. Finance Manager (can see all invoices, reconcile)
8. Finance User (can see invoices only, read-only)
9. Intern (can see products only)
Step 2: Create Groups in Odoo
Settings → Users & Groups → Groups
For each group, specify which models they access:
| Group | Models & Permissions |
|---|---|
| Sales Rep | sale.order: read, write, create res.partner: read only (contact info, no pricing) stock.picking: read only |
| Warehouse Staff | stock.move: read, write stock.quant: read only (inventory levels) sale.order: read only res.partner: no access |
| Finance | account.move: read, write, create account.reconcile: read, write sale.order: read only res.partner: read only |
Step 3: Create Record Rules
<!-- Sales reps see only their orders -->
<field name="domain_force">[('user_id', '=', user.id)]</field>
<!-- Warehouse staff see only their warehouse -->
<field name="domain_force">[('warehouse_id', '=', user.warehouse_id.id)]</field>
<!-- Finance sees only their company -->
<field name="domain_force">[('company_id', '=', user.company_id.id)]</field>
Step 4: Hide Sensitive Fields
<!-- Hide cost field from sales team -->
<field name="groups">account.group_account_manager</field>
<!-- Hide employee salary from non-HR -->
<field name="groups">hr.group_hr_manager</field>
Step 5: Test Everything
Log in as each role. Verify:
✓ Can see what they should see
✓ Cannot see what they shouldn't see
✓ Cannot delete what they shouldn't delete
Results After Implementation
| Metric | Result |
|---|---|
| Data integrity | Sales reps no longer "accidentally" modify commission rates |
| Compliance | Finance data is isolated from operations |
| Auditability | Every change is logged with user/timestamp |
| Productivity | Staff can't get lost in data they don't need |
| Security incident risk | Dropped 90% |
Common RBAC Pitfalls
| Pitfall | Problem | Fix |
|---|---|---|
| Over-permissioning | Finance gets full access to orders, now deletes by accident | Give read-only access to orders |
| Not testing record rules | Salesman still sees all orders (rule misconfigured) | Log in as each role, try to see what you shouldn't |
| Admin bypass | Give someone "admin" to fix one thing, they see everything forever | Delete admin access immediately after fixing |
| Too restrictive | Warehouse can't see orders from main warehouse when needed | Balance security with functionality, use OR logic |
Your Action Items
This Week
❏ List every role in your company (don't use "admin" or "user")
❏ For each role, define: What models do they touch?
❏ Identify: What data should they NOT see?
Next Week
❏ Create groups in Odoo (one per role)
❏ Assign users to groups
❏ Define access rights (read/write/create/delete)
❏ Create record rules for data visibility
Testing
❏ Log in as each role
❏ Try to see something you shouldn't (you should get "access denied")
❏ Try to delete something (should fail unless permitted)
❏ Check audit log (every change should show who made it)
Ongoing
❏ When you hire someone, assign correct group immediately
❏ When someone changes roles, update group membership
❏ Monthly audit: Check if anyone has overly broad access
❏ When incidents happen, tighten the rules
The Insurance Reality
Real talk: If you get hacked or have a data breach, your insurance company will ask: "Did you have role-based access control?"
If the answer is no, they won't pay.
We've seen founders lose $250K+ because they didn't implement proper access control. Not from malicious insiders—from careless employees who had too much power.
Your team isn't trying to sabotage you. They're just humans. Humans make mistakes. RBAC prevents those mistakes from becoming catastrophes.
Free Access Control Audit
Stop waiting for disaster to implement this. We'll review your current setup and identify exactly where someone could accidentally (or intentionally) cause damage. Takes 2 hours. Costs nothing. Your competitors are locked down. Your business shouldn't be wide open.
