Odoo Development Environment: Best Practices for Building Custom Modules
Odoo is a powerful and highly customizable ERP platform that allows businesses to adapt standard applications to their specific processes. When standard Odoo functionality is not enough, developers can create custom modules to introduce new business logic, fields, workflows, reports, integrations, and user interfaces.
However, successful Odoo development is not simply about writing Python and XML code. A well-built custom module should be secure, maintainable, upgrade-friendly, testable, performant, and easy for other developers to understand.
A properly configured development environment plays an important role in achieving these goals.
Odoo's modular architecture allows developers to extend existing applications or create entirely new applications using the Odoo framework. Its developer documentation provides dedicated guidance for module structure, ORM development, security, testing, views, controllers, and coding standards.
This guide explains the best practices businesses and Odoo developers should follow when building custom modules.
What Is an Odoo Custom Module?
An Odoo custom module is an independent package that extends or introduces functionality within an Odoo database.
A custom module can:
Create new business models
Add fields to existing models
Modify existing workflows
Create new menus and views
Add reports
Implement automated processes
Create website functionality
Add API integrations
Extend existing Odoo applications
Introduce custom security rules
Add JavaScript and frontend functionality
For example, a manufacturing company might need a Production Approval module that adds an additional approval step before a Manufacturing Order can be confirmed.
Instead of modifying Odoo's core source code, developers can create a custom module that extends the existing Manufacturing application.
This approach keeps custom functionality separated from the standard Odoo codebase.
Why a Proper Odoo Development Environment Matters
A development environment provides a safe place to build and test customizations before they reach production.
Without a proper development process, developers may accidentally:
Modify production data
Break existing workflows
Introduce security vulnerabilities
Create incompatible customizations
Make upgrades difficult
Introduce performance problems
Lose source-code changes
Deploy untested functionality
A good Odoo development environment should therefore separate:
Development → Testing → Staging → Production
Developers should build and test changes before deploying them to the live system.
Recommended Odoo Development Environment
A typical professional Odoo development environment can include:
| Component | Purpose |
|---|---|
| Odoo Source Code | Application framework |
| Python | Backend development |
| PostgreSQL | Database |
| Git | Source-code management |
| IDE/Code Editor | Development |
| Virtual Environment | Python dependency isolation |
| Testing Database | Functional testing |
| Staging Environment | Pre-production validation |
| Production Environment | Live ERP system |
Depending on the project, developers may also use:
Docker
Linux
Odoo.sh
CI/CD pipelines
Automated testing
PostgreSQL administration tools
Debugging tools
Odoo's developer tutorials include guidance for running the server, enabling developer mode, using Git, Python debugging, and PostgreSQL administration tools.
1. Keep Development, Staging, and Production Separate
One of the most important practices is to avoid developing directly on the production server.
A recommended architecture is:
Development
Used by developers to:
Write code
Create models
Modify views
Test features
Debug errors
Staging
Used to:
Test the complete module
Validate integrations
Test realistic workflows
Perform user acceptance testing
Production
Used only for:
Live business operations
Approved releases
Production data
A simplified workflow is:
Developer → Git → Testing → Staging → Approval → Production
This significantly reduces the risk of deploying unfinished code.
2. Use Git for Odoo Development
Every serious Odoo customization project should use version control.
Git allows developers to track:
Code changes
New features
Bug fixes
Configuration changes
Module versions
Developer contributions
A typical workflow might be:
main │ ├── develop │ ├── feature/customer-approval │ ├── feature/custom-report │ └── fix/inventory-validation │ └── release
Developers can create separate branches for individual features and merge them after testing.
Benefits of Git
Git helps teams:
Track changes
Review code
Revert mistakes
Collaborate
Maintain release history
Identify when bugs were introduced
It is particularly valuable for large Odoo projects where multiple developers work on the same custom modules.
3. Follow a Standard Odoo Module Structure
A custom module should have a clear and predictable structure.
For example:
custom_sales_extension/
│
├── __init__.py
├── __manifest__.py
│
├── models/
│ ├── __init__.py
│ └── sale_order.py
│
├── views/
│ ├── sale_order_views.xml
│ └── menu_views.xml
│
├── security/
│ ├── ir.model.access.csv
│ └── security.xml
│
├── data/
│ └── data.xml
│
├── wizard/
│ ├── __init__.py
│ └── approval_wizard.py
│
├── report/
│ └── sale_report.xml
│
├── static/
│ ├── src/
│ └── tests/
│
└── tests/
├── __init__.py
└── test_sale_order.py
Odoo's coding guidelines recommend organizing module functionality into directories such as models, controllers, views, data, static, wizard, report, and tests where appropriate.
A clean structure makes the module easier to understand, debug, maintain, and upgrade.
4. Maintain a Proper __manifest__.py
The __manifest__.py file defines important metadata about an Odoo module.
It can specify:
Module name
Version
Dependencies
Author
Category
Description
Data files
Demo files
Assets
Installation behavior
A simple example is:
{
'name': 'Custom Sales Extension',
'version': '1.0',
'depends': ['sale'],
'category': 'Sales',
'description': 'Custom sales functionality',
'data': [
'security/ir.model.access.csv',
'views/sale_order_views.xml',
],
'installable': True,
}
Odoo documentation specifies that __manifest__.py declares a Python package as an Odoo module and defines its metadata and dependencies. Dependencies are loaded before the module itself.
Best Practice
Keep dependencies minimal.
If your module only requires sale, avoid unnecessarily depending on several unrelated applications.
This reduces coupling and can make upgrades easier.
5. Use the Odoo ORM Properly
The Object-Relational Mapping (ORM) layer is one of the most important parts of Odoo development.
Instead of directly manipulating database tables, developers generally interact with Odoo models through the ORM.
For example:
orders = self.env['sale.order'].search([
('state', '=', 'sale')
])
The ORM handles important Odoo behaviors including:
Access rights
Record rules
Field behavior
Relationships
Computed fields
Caching
Database operations
Odoo's security documentation specifically warns against bypassing the ORM when it can perform the required operation, because direct database access can bypass important framework behavior and security mechanisms.
6. Avoid Unnecessary Raw SQL
Direct SQL should not be the first solution when the Odoo ORM can handle the requirement.
Avoid patterns such as:
self.env.cr.execute(...)
unless there is a genuine technical reason to use SQL.
Prefer ORM methods such as:
search() create() write() unlink() read() browse()
For example:
customers = self.env['res.partner'].search([
('customer_rank', '>', 0)
])
This is generally easier to maintain and works with Odoo's security and framework mechanisms.
Odoo's security documentation explicitly recommends using the ORM rather than bypassing it with direct database cursor operations whenever possible.
7. Design Models Carefully
Before writing Python code, developers should understand the business requirement and design the model structure.
Consider:
Model
custom.approval
Fields
name user_id approval_date state notes
States
Draft Submitted Approved Rejected
Good model design helps prevent unnecessary complexity later.
Developers should also consider:
Field types
Relationships
Required fields
Default values
Computed fields
Stored fields
Constraints
Access permissions
8. Use Inheritance Instead of Modifying Odoo Core
One of the most important Odoo development principles is to extend existing models rather than modify the Odoo core code directly.
For example:
from odoo import models, fields
class SaleOrder(models.Model):
_inherit = 'sale.order'
customer_reference = fields.Char(
string='Customer Reference'
)
This extends the existing Sales Order model.
It is preferable to modifying the original Odoo source file.
Why?
Core modifications can:
Make upgrades difficult
Create conflicts
Complicate debugging
Make maintenance expensive
Increase technical debt
Custom modules provide a cleaner separation between standard Odoo functionality and business-specific requirements.
9. Keep Business Logic in Python
Complex business logic should generally be implemented in models rather than hidden inside XML views or JavaScript.
For example:
def action_approve(self):
for record in self:
if not record.user_id:
raise UserError(
"An approver must be assigned."
)
record.state = 'approved'
This makes the logic reusable from:
Forms
Automated actions
Other Python methods
APIs
Scheduled operations
It also makes the business process easier to test.
10. Keep Views Clean and Maintainable
Odoo views are commonly defined using XML.
For example:
<record id="view_custom_approval_form" model="ir.ui.view">
<field name="name">custom.approval.form</field>
<field name="model">custom.approval</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
<field name="user_id"/>
<field name="state"/>
</group>
</sheet>
</form>
</field>
</record>
Good XML should be:
Structured
Readable
Consistently named
Easy to inherit
Free from unnecessary duplication
Avoid putting excessive business logic into the user interface.
11. Implement Security from the Beginning
Security should not be added at the end of the development process.
Odoo provides mechanisms including:
User groups
Access Control Lists
Record rules
Field-level restrictions
Access rights determine whether users can:
Read
Create
Write
Delete
records for a model.
These permissions are commonly defined using ir.model.access.csv.
For example:
Sales User
↓
Read + Create
Sales Manager
↓
Read + Create + Write + Delete
Developers should test the module using different user roles rather than testing only as Administrator.
12. Never Assume Hidden Fields Mean Security
A field being invisible in a view does not automatically mean the underlying data is protected.
Security should be implemented using:
Access rights
Record rules
Groups
Field-level restrictions where appropriate
Odoo's security documentation distinguishes between visibility and actual access control and explains that field restrictions can be applied using the field's groups attribute.
This is particularly important when developing modules containing:
Financial information
Employee information
Customer data
Pricing
Confidential documents
Management approvals
13. Be Careful With sudo()
The sudo() method can bypass normal access rights and record rules.
For example:
records = self.env['sale.order'].sudo().search([])
Although this can be useful in specific situations, it should not be used simply to make access errors disappear.
Odoo documentation warns that bypassing security mechanisms should be done carefully and only when the operation legitimately requires it.
Before using sudo(), ask:
Should this operation really bypass the current user's permissions?
If the answer is no, fix the underlying access configuration instead.
14. Follow Odoo Coding Guidelines
Consistent coding style makes custom modules easier to maintain.
Odoo's official coding guidelines are designed to improve:
Readability
Maintainability
Debugging
Reliability
Code quality
Complexity management
The guidelines apply to new modules and new development.
Developers should maintain consistent:
File names
Class names
Method names
Variable names
XML IDs
Module structure
Python formatting
For community modules, Odoo also recommends using a company or organization prefix in the module name to reduce naming conflicts.
15. Write Automated Tests
Testing should be part of the development process, not something performed only after development is complete.
Odoo supports different types of tests, including:
Python unit tests
JavaScript tests
Integration tests and tours
Odoo's testing documentation describes these approaches and provides a framework for implementing module tests.
A module might contain:
tests/ ├── __init__.py ├── test_approval.py └── test_security.py
Tests can verify scenarios such as:
Approval workflow
Calculations
Access permissions
Record creation
Business constraints
Automated actions
16. Test Business Scenarios, Not Just Code
A common mistake is testing only whether a Python method executes successfully.
Instead, test the actual business process.
For example, for a custom purchase approval module:
Scenario 1
Purchase Order below ₹50,000.
Expected: No management approval required.
Scenario 2
Purchase Order above ₹50,000.
Expected: Manager approval required.
Scenario 3
Unauthorized user attempts approval.
Expected: Access denied.
Scenario 4
Approved order is modified.
Expected: Approval workflow behaves according to business rules.
This approach produces more useful tests.
Odoo's testing guidance recommends focusing business-module tests on business flows rather than unnecessarily testing framework functionality that is already covered by Odoo itself.
17. Consider Performance From the Beginning
Poorly designed custom modules can slow down an Odoo database.
Common performance problems include:
Excessive database queries
Searching inside loops
Unnecessary computed fields
Poorly designed domains
Excessive use of sudo()
Large unoptimized reports
Inefficient SQL
Loading unnecessary records
For example, avoid repeatedly performing searches inside a large loop when records can be retrieved more efficiently.
Instead of thinking:
"Does this code work?"
developers should also ask:
"How will this behave with 10 records, 10,000 records, or 1 million records?"
Performance becomes particularly important for:
Inventory
Accounting
Manufacturing
Sales
Large customer databases
High-volume integrations
18. Keep Customizations Modular
Avoid creating one giant custom module containing every customization.
For example, instead of:
company_customization
containing 100 unrelated features, consider separating functionality:
company_sales company_inventory company_manufacturing company_accounting company_hr
This makes it easier to:
Maintain features
Test changes
Deploy updates
Identify dependencies
Remove obsolete functionality
Modularity is one of the fundamental strengths of Odoo's architecture.
19. Document Custom Modules
Every custom module should have documentation.
At minimum, document:
Purpose
Business requirements
Dependencies
Installation instructions
Configuration
User roles
Important workflows
External integrations
Scheduled actions
Known limitations
For example:
Module: Custom Purchase Approval Purpose: Adds approval workflow for purchase orders above configured thresholds. Dependencies: purchase User Roles: Purchase User Purchase Manager Approval Rule: > ₹50,000 → Manager Approval > ₹250,000 → Director Approval
Documentation can save significant time when another developer needs to maintain the module later.
20. Use Meaningful XML IDs
XML IDs should be descriptive.
Prefer:
view_custom_approval_form action_custom_approval menu_custom_approval
instead of:
view1 action1 menu1
Meaningful IDs make debugging and module inheritance easier.
21. Avoid Hard-Coding Business Values
Instead of writing:
if amount > 50000:
consider making the threshold configurable.
For example:
Purchase Approval Limit ₹50,000
This allows administrators to change the business rule without modifying source code.
Configurable values can be managed through appropriate Odoo configuration mechanisms depending on the requirement.
22. Use Developer Mode Carefully
Developer mode is extremely useful during development.
It provides access to technical information such as:
Models
Fields
Views
Actions
Menus
External IDs
Technical settings
Developers can use it to understand how standard Odoo applications work before extending them.
However, technical settings should be restricted to appropriate users in production environments.
23. Debug Systematically
When an Odoo customization fails, developers should identify the problem systematically.
A useful process is:
Reproduce the Error
↓
Read the Server Log
↓
Identify the Model / View / Method
↓
Check Dependencies
↓
Check Access Rights
↓
Check Data
↓
Fix the Root Cause
↓
Run Tests
↓
Deploy to Staging
Avoid repeatedly making random changes until the error disappears.
24. Keep Dependencies Under Control
The more modules your custom module depends on, the more tightly coupled it becomes to the Odoo environment.
For example:
'depends': [
'sale',
'stock',
'purchase',
]
Only include dependencies that are actually required.
Odoo installs dependencies before the dependent module, so dependency definitions directly influence module loading and installation behavior.
25. Prepare for Odoo Upgrades
Custom modules should be developed with future upgrades in mind.
Avoid unnecessary modifications to:
Odoo core files
Standard JavaScript
Standard views
Standard workflows
Instead, use supported extension mechanisms such as:
Model inheritance
View inheritance
Extension modules
Configuration
Standard APIs
A customization that works today but prevents a future Odoo upgrade can become expensive technical debt.
26. Recommended Custom Module Development Workflow
A professional Odoo development process can look like this:
Step 1: Understand the Requirement
Document:
Business problem
Users
Workflow
Expected result
Exceptions
Step 2: Check Standard Odoo
Before writing custom code, determine whether the requirement can be solved through:
Configuration
Studio
Existing Odoo functionality
Automated actions
Standard modules
Step 3: Design the Solution
Define:
Models
Fields
Views
Security
Workflow
Dependencies
Step 4: Create the Module
Build the module using Odoo's standard structure.
Step 5: Implement Business Logic
Use Python and the ORM.
Step 6: Implement the UI
Create XML views, menus, actions, and required frontend components.
Step 7: Configure Security
Add access rights and record rules.
Step 8: Write Tests
Test the important business scenarios.
Step 9: Code Review
Review:
Security
Performance
Naming
Maintainability
Dependencies
Step 10: Deploy to Staging
Test with realistic data and workflows.
Step 11: User Acceptance Testing
Allow business users to validate the functionality.
Step 12: Production Deployment
Deploy only after approval and backup.
Example: Custom Odoo Customer Approval Module
Consider a company that wants every new customer to be approved before salespeople can confirm quotations.
Standard Odoo
Salespeople can create customers and quotations.
Business Requirement
New customers must be approved by a Sales Manager.
Custom Module
The module introduces:
Customer Status
Draft
↓
Submitted
↓
Approved
↓
Rejected
Additional Features
The module could add:
Approval button
Rejection button
Approval date
Approver
Rejection reason
Customer approval status
Sales restrictions
Security
Sales users:
Create customer
Submit for approval
View approval status
Sales managers:
Approve
Reject
Modify approval information
Testing
Tests verify:
Customer creation
Submission
Approval
Rejection
Unauthorized approval
Sales restriction
This is a good example of where a custom module can extend standard Odoo functionality without modifying the core system.
Common Odoo Development Mistakes
1. Modifying Odoo Core
This creates upgrade and maintenance problems.
Better: Use custom modules and inheritance.
2. Ignoring Security
A feature may work correctly for an Administrator but expose data to normal users.
Better: Design and test access controls from the beginning.
3. Excessive sudo()
Using sudo() everywhere can unintentionally bypass security.
Better: Use it only when there is a legitimate business reason.
4. Using Raw SQL Unnecessarily
Direct SQL can bypass important Odoo mechanisms.
Better: Use the ORM whenever practical.
5. No Automated Tests
Untested customizations are more likely to break after changes or upgrades.
Better: Add tests for important business workflows.
6. Poor Module Structure
Disorganized files make future maintenance difficult.
Better: Follow Odoo's recommended module structure.
7. Hard-Coding Business Rules
Business requirements often change.
Better: Make appropriate values configurable.
8. Testing Only as Administrator
Administrator access can hide security problems.
Better: Test with realistic user roles.
9. No Documentation
Future developers may not understand why a customization exists.
Better: Document the business purpose and technical design.
Odoo Custom Development Best Practices Checklist
Before deploying a custom module, verify:
Requirement is clearly documented
Standard Odoo functionality has been evaluated
Module has a clean structure
__manifest__.py is correctly configured
Dependencies are minimal
Odoo ORM is used appropriately
Core files are not unnecessarily modified
Access rights are configured
Record rules are reviewed
sudo() is used only when justified
Views are clean and maintainable
Business logic is separated from UI
Automated tests are included
Performance has been considered
Git version control is used
Documentation is available
Staging testing is completed
User acceptance testing is completed
Production backup is available
Deployment and rollback procedures are defined
Benefits of Following Odoo Development Best Practices
A disciplined development approach provides long-term benefits.
Better Code Quality
Developers can understand and maintain the code more easily.
Improved Security
Users receive only the access they need.
Easier Upgrades
Custom functionality remains separated from standard Odoo code.
Better Performance
Efficient ORM usage and optimized business logic reduce unnecessary database operations.
Faster Troubleshooting
Clear structure and documentation make problems easier to identify.
Easier Team Collaboration
Git, consistent naming, and modular architecture allow multiple developers to work together effectively.
Lower Technical Debt
Well-designed custom modules are easier to maintain as the business grows.
How ERP Harbor Consulting Services Can Help
Custom Odoo development should focus on solving business problems while keeping the resulting solution maintainable and upgrade-friendly.
ERP Harbor Consulting Services can help businesses with:
Odoo custom module development
Odoo customization
Odoo application development
Python development
XML and view customization
JavaScript/frontend customization
Odoo API integrations
Third-party integrations
Workflow automation
Manufacturing customization
Inventory customization
Sales and Purchase customization
Accounting customization
Reporting and dashboards
Odoo migration and upgrades
Performance optimization
Testing and debugging
Odoo maintenance and support
Our development approach focuses on understanding the business requirement first, evaluating standard Odoo functionality, and then building custom functionality where it provides genuine business value.
Conclusion
Building an Odoo custom module is more than adding a few Python models or modifying an XML view. A successful customization requires a well-structured development environment, clean architecture, secure access control, proper ORM usage, automated testing, version control, performance considerations, and upgrade-friendly design.
Odoo's modular architecture makes it possible to extend the ERP without unnecessarily modifying the core platform. By following official development guidelines and adopting professional development practices, businesses can create custom modules that remain reliable as their operations and Odoo environment evolve.
The goal should not simply be to make a customization work today.
The goal should be to build an Odoo solution that is secure, maintainable, scalable, testable, and ready for the future.