Appearance
Common Mistakes & How to Avoid Them
Learning from Mistakes
This section covers the most common mistakes made by functional consultants and how to avoid them. Understanding these pitfalls will save you hours of troubleshooting and help you design better solutions.
Data Model Mistakes
Mistake 1: Using Non-Stored Fields in Group By
Wrong Approach
"I want to group orders by customer country"
Uses: partner_id.country_id (related, not stored)
Result: Error or empty grouping
Correct Approach
Create a stored related field:
python
partner_country_id = fields.Many2one(
related='partner_id.country_id',
store=True
)Result: Works perfectly
Why this happens: Group By uses SQL GROUP BY which requires actual database columns. Non-stored fields have no column.
Mistake 2: Confusing Field Type and Widget
Wrong Thinking
"I need a radio button field"
Creates a new field type
Correct Understanding
"I need a Selection field displayed as radio buttons"
Uses: Selection field with widget="radio" in the view
Key insight: Field type = how data is stored. Widget = how data is displayed.
| What You Want | Field Type | Widget |
|---|---|---|
| Radio buttons | Selection | radio |
| Star rating | Selection | priority |
| Progress bar | Float | progressbar |
| Tags | Many2many | many2many_tags |
| User avatar | Many2one | many2one_avatar_user |
Mistake 3: Over-Using Many2many
Wrong Design
"Product can have multiple suppliers"
Uses: Many2many between product and supplier
Problem: Can't store price per supplier!
Correct Design
Create a "Product Supplier" intermediate model with:
- Many2one to product
- Many2one to supplier
- Price field
- Lead time field
- Minimum quantity field
This is what product.supplierinfo does.
Rule: If you need ANY extra data about the relationship, use an intermediate model.
Mistake 4: Forgetting ondelete
The Problem
You have orders linked to customers. If someone deletes a customer, what happens to their orders?
| ondelete Value | What Happens | When to Use |
|---|---|---|
set null (default) | Field becomes empty | Rarely appropriate |
cascade | Related records deleted too | Parent-child only |
restrict | Deletion blocked | Important relationships |
Real Consequences
- set null: Orders remain but "orphaned" - customer field is empty, data corrupted
- cascade: All customer's orders deleted - massive data loss
- restrict: Safe - forces user to handle orders first
Best Practice: Always specify ondelete='restrict' for important business relationships.
Mistake 5: Creating Circular Dependencies
Wrong Design
python
# Model A
b_id = fields.Many2one('model.b')
# Model B
a_id = fields.Many2one('model.a')Both required = can't create either record first!
Solutions
- Make one field optional
- Create records in two steps (save draft, then link)
- Rethink the relationship (maybe one should be computed)
Automation Mistakes
Mistake 6: Creating Infinite Loops
Wrong Automation
Automated Action on sale.order:
- Trigger: On Update
- Action: Update a field on the same order
Result: Update triggers automation, which triggers update, which triggers automation... 🔄
Correct Approach
- Use Trigger Fields to limit when automation fires
- Don't update fields that trigger the same automation
- Use a "processing" flag to prevent re-entry
Mistake 7: Expecting AI to Replace Everything
Wrong Assumption
"Odoo 19 AI will predict lead conversion using GPT"
Reality: Lead probability still uses Bayesian PLS algorithm, not LLM
Correct Understanding
AI in Odoo 19 is for:
- Text generation (emails, descriptions)
- Document digitization (OCR)
- Chat assistants
PLS (Predictive Lead Scoring) remains statistical/Bayesian
AI ≠ Machine Learning ≠ Statistical Models
Mistake 8: Using Server Actions for Everything
Wrong Pattern
Using Server Actions for processes that should be automated:
- Daily cleanup → Should be Scheduled Action
- On-save notifications → Should be Automated Action
| Use Case | Correct Action Type |
|---|---|
| User clicks button | Server Action |
| Record created/updated | Automated Action |
| Daily/weekly task | Scheduled Action |
| Bulk update | Server Action |
| Email on status change | Automated Action |
Security Mistakes
Mistake 9: Using Domains Instead of Record Rules
Wrong Approach
"Salespeople should only see their leads"
Uses: Domain filter in the view
Problem: Power users can remove the filter, API access ignores it
Correct Approach
Use a Record Rule:
python
[('user_id', '=', user.id)]Result: Cannot be bypassed by any means
Rule: If it's a security requirement, use Record Rules. If it's just convenience, use Domains.
Mistake 10: Giving Too Many Admin Rights
Wrong Approach
"User needs to see all orders" → Makes them Sales Manager
Problem: Manager can also delete, configure, access reports they shouldn't
Correct Approach
- Create custom group: "Sales Viewer"
- Grant only Read access to sale.order
- Add record rule if needed for filtering
View Mistakes
Mistake 11: Not Using Tracking
Best Practice
For important fields (status, assigned user, amounts), add tracking=True.
This creates an audit trail in the chatter showing who changed what and when.
Critical for: Financial fields, status changes, assignments
Mistake 12: Ignoring Performance in List Views
Wrong Approach
Adding many computed/related fields to list view without storing them
Result: Slow list loads, timeout errors
Correct Approach
- Check if fields are stored: Settings → Technical → Fields
- Add
store=Trueto computed/related fields in list views - Limit list view to essential columns
Mistake 13: Making Everything Required
Over-Restriction
Making 15 fields required on a form
Result: Users can't save drafts, abandon forms, work around by entering garbage data
Balanced Approach
- Required at model level: Only truly mandatory fields
- Required at state: Add requirements as workflow progresses
- Required in view: For specific contexts only
xml
<field name="delivery_date" required="state == 'confirmed'"/>Configuration Mistakes
Mistake 14: Testing in Production
Never Do This
"Let me just try this automation in production..."
Risk: Bad automation can corrupt data, send emails to customers, break workflows
Always
- Test in staging first
- Use Odoo.sh or create test database
- Verify with sample data
- Only then deploy to production
Mistake 15: Not Documenting Changes
Common Problem
Making Studio changes or configurations without documentation
Six months later: "Why is this field here? What does this automation do?"
Best Practices
| Change Type | Document Where |
|---|---|
| Field purpose | Field help text |
| Automation logic | Automation description field |
| View modifications | Changelog document |
| Custom reports | Report description |
| Studio changes | Export and version control |
Integration Mistakes
Mistake 16: Syncing Too Frequently
Over-Integration
Syncing data every minute when hourly is sufficient
Result: Performance degradation, API rate limits, wasted resources
Right Approach
| Data Type | Sync Frequency |
|---|---|
| Real-time critical (payments) | Webhook/immediate |
| Orders | Every 15-30 minutes |
| Product catalog | Daily |
| Historical data | Weekly/monthly |
Mistake 17: Not Handling Errors
Wrong Implementation
API integration that fails silently
Result: Missing orders, duplicate records, data corruption - discovered weeks later
Correct Implementation
- Log all API calls and responses
- Create error records for failures
- Set up alerts for repeated failures
- Implement retry logic with backoff
Quick Reference: Error Messages and Solutions
| Error Message | Likely Cause | Solution |
|---|---|---|
| "Field X does not exist" | Typo in field name or module not installed | Check spelling, install module |
| "Access denied" | User lacks permissions | Check access rights and record rules |
| "Cannot group by X" | Field not stored | Add store=True or use different field |
| "Constraint violation" | Required field empty or unique violation | Fill required fields, check uniqueness |
| "Recursion error" | Circular dependency in computed fields | Check @api.depends chains |
| "Record does not exist" | Deleted or wrong ID | Check if record was deleted |
| "Singleton error" | Operation on multiple records expected one | Use ensure_one() or loop |
| "Validation error" | Python constraint failed | Check @api.constrains logic |
| "Integrity error" | Database constraint violated | Check foreign keys, unique constraints |
Checklist: Before Going Live
Data Model Review
- [ ] All relational fields have appropriate
ondelete - [ ] Stored fields for list views and filters
- [ ] No circular dependencies
- [ ] Proper use of Many2many vs intermediate models
Security Review
- [ ] Record rules for data isolation
- [ ] Access rights per group
- [ ] No excessive admin permissions
- [ ] Tested with different user roles
Automation Review
- [ ] No infinite loop potential
- [ ] Trigger fields specified
- [ ] Error handling configured
- [ ] Tested in staging environment
Performance Review
- [ ] List view fields are stored
- [ ] Indexes on frequently searched fields
- [ ] No complex record rules
- [ ] Reasonable automation triggers
Knowledge Check
Q1: Why can't you group by partner_id.country_id directly?
Answer: It's not stored in the database
Related fields without store=True are computed on-the-fly. SQL GROUP BY requires an actual column. Create a stored related field instead.
Q2: What's wrong with Many2many for Product-Supplier?
Answer: Can't store per-supplier data like price
Many2many only links records. It can't store price, lead time, or other relationship-specific data. Use an intermediate model instead.
Q3: User can clear a domain filter and see all records. Security issue?
Answer: Yes - use Record Rules for security
Domains are for convenience. Record Rules enforce security that cannot be bypassed through UI, API, or any other means.
Q4: Automation triggers itself endlessly. How to fix?
Answer: Add Trigger Fields to limit when it fires
Specify which fields should trigger the automation. If updating field X, don't include X in trigger fields (or use a processing flag).
Q5: When should ondelete='cascade' be used?
Answer: Only for true parent-child relationships where children can't exist alone
Example: Order Lines should be deleted when Order is deleted. But Orders should NOT be deleted when Customer is deleted (use restrict).