Odoo Module Creator
Overview
This skill enables creation of complete, production-ready Odoo 16.0 Enterprise modules with proper directory structure, manifest files, models, views, security configurations, and documentation. It follows OCA guidelines and Siafa project standards.
Module Creation Workflow
Step 1: Gather Module Requirements
Ask clarifying questions to collect essential information:
- •Module technical name (snake_case format, e.g.,
stock_batch_tracking,pos_custom_receipt) - •Module display name (human-readable, e.g., "Stock Batch Tracking", "POS Custom Receipt")
- •Module purpose (1-2 sentence description of functionality)
- •Module category (select from: Sales, Inventory, Accounting, Point of Sale, Human Resources, Manufacturing, Purchases, Warehouse, Website, etc.)
- •Dependencies (base modules required, e.g.,
stock,account,point_of_sale) - •Module type (see Module Types section below)
- •Target addon directory (e.g.,
addons-stock,addons-pos,addons-account)
Step 2: Determine Module Type
Identify which type of module to create based on the purpose:
A. Simple Model Module - CRUD operations for a new business entity
- •Creates new models with fields and views
- •Example: Customer feedback tracking, equipment registry
B. Extension Module - Extends existing Odoo models
- •Inherits and adds fields/methods to existing models
- •Example: Add serial number tracking to stock.picking
C. POS Customization - Point of Sale enhancements
- •Extends POS models, screens, or receipts
- •Example: Custom receipt format, loyalty points integration
D. Stock/Inventory Enhancement - Warehouse and inventory features
- •Stock valuation, warehouse operations, batch tracking
- •Example: Inter-warehouse transit, GRN-invoice linking
E. Accounting Customization - Financial module extensions
- •Account moves, vendor bills, analytic accounting
- •Example: Multi-dimensional analytics, custom invoicing
F. Report Module - Custom reports (PDF, Excel)
- •QWeb templates, data aggregation, export functionality
- •Example: Sales analysis, inventory valuation reports
G. Integration Module - External API/service connectors
- •REST API clients, webhooks, data synchronization
- •Example: Beatroute connector, payment gateway integration
H. Widget/UI Customization - Frontend enhancements
- •JavaScript widgets, custom views, web controllers
- •Example: Kanban view customizations, dashboard widgets
Step 3: Generate Module Structure
Create the complete directory structure with all required files:
module_name/
├── __init__.py
├── __manifest__.py
├── models/
│ ├── __init__.py
│ └── [model_files].py
├── views/
│ ├── [model]_views.xml
│ └── menu_views.xml
├── security/
│ ├── security_groups.xml (if needed)
│ └── ir.model.access.csv
├── data/ (optional)
│ └── data.xml
├── wizards/ (if needed)
│ ├── __init__.py
│ └── [wizard_name].py
├── report/ (if reports needed)
│ ├── __init__.py
│ ├── [report_name].py
│ └── templates/
│ └── [report_template].xml
├── static/
│ ├── description/
│ │ ├── icon.png
│ │ └── index.html
│ └── src/ (for JS/CSS if needed)
│ ├── js/
│ └── css/
└── tests/ (recommended)
├── __init__.py
└── test_[module].py
Step 4: Generate manifest.py
Create manifest with standard metadata:
{
'name': '[Module Display Name]',
'version': '16.0.1.0.0',
'category': '[Category]',
'summary': '[Brief one-line description]',
'description': """
[Detailed multi-line description of module functionality]
Key Features:
- Feature 1
- Feature 2
- Feature 3
""",
'author': 'Jamshid K',
'website': 'https://siafadates.com',
'license': 'LGPL-3',
'depends': [
'base',
# Additional dependencies
],
'data': [
'security/security_groups.xml', # Load first
'security/ir.model.access.csv',
'views/[model]_views.xml',
'views/menu_views.xml',
'data/data.xml', # If needed
'report/templates/[report].xml', # If needed
],
'assets': { # If JS/CSS needed
'web.assets_backend': [
'module_name/static/src/js/*.js',
'module_name/static/src/css/*.css',
],
},
'demo': [], # Demo data if applicable
'installable': True,
'auto_install': False,
'application': False, # True for standalone apps
}
Step 5: Generate Model Files
Create model files following Odoo ORM best practices:
from odoo import models, fields, api
from odoo.exceptions import UserError, ValidationError
import logging
_logger = logging.getLogger(__name__)
class ModelName(models.Model):
"""Description of the model."""
_name = 'module.model'
_description = 'Model Description'
_inherit = ['mail.thread', 'mail.activity.mixin'] # If needed
_order = 'create_date desc'
# Fields
name = fields.Char(
string='Name',
required=True,
index=True,
tracking=True,
help='Primary identifier for this record'
)
active = fields.Boolean(
string='Active',
default=True,
help='If unchecked, this record will be hidden'
)
state = fields.Selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('done', 'Done'),
('cancel', 'Cancelled'),
], string='Status', default='draft', required=True, tracking=True)
company_id = fields.Many2one(
'res.company',
string='Company',
required=True,
default=lambda self: self.env.company
)
# Relational fields
partner_id = fields.Many2one('res.partner', string='Partner')
line_ids = fields.One2many('module.model.line', 'parent_id', string='Lines')
# Computed fields
total_amount = fields.Float(
string='Total Amount',
compute='_compute_total_amount',
store=True
)
# Constraints
_sql_constraints = [
('name_unique', 'UNIQUE(name, company_id)', 'Name must be unique per company!'),
]
@api.depends('line_ids', 'line_ids.amount')
def _compute_total_amount(self):
"""Compute total amount from lines."""
for record in self:
record.total_amount = sum(record.line_ids.mapped('amount'))
@api.onchange('partner_id')
def _onchange_partner_id(self):
"""Update fields when partner changes."""
if self.partner_id:
# Logic here
pass
@api.constrains('total_amount')
def _check_total_amount(self):
"""Validate total amount is positive."""
for record in self:
if record.total_amount < 0:
raise ValidationError('Total amount must be positive!')
def action_confirm(self):
"""Confirm the record."""
self.ensure_one()
if self.state != 'draft':
raise UserError('Only draft records can be confirmed!')
self.write({'state': 'confirmed'})
_logger.info('Record %s confirmed by user %s', self.name, self.env.user.name)
Step 6: Generate View Files
Create XML view definitions:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Tree View -->
<record id="view_model_tree" model="ir.ui.view">
<field name="name">module.model.tree</field>
<field name="model">module.model</field>
<field name="arch" type="xml">
<tree string="Model Name">
<field name="name"/>
<field name="partner_id"/>
<field name="state" decoration-info="state == 'draft'"
decoration-success="state == 'done'"/>
<field name="total_amount" sum="Total"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<!-- Form View -->
<record id="view_model_form" model="ir.ui.view">
<field name="name">module.model.form</field>
<field name="model">module.model</field>
<field name="arch" type="xml">
<form string="Model Name">
<header>
<button name="action_confirm" string="Confirm" type="object"
class="oe_highlight" states="draft"/>
<field name="state" widget="statusbar"
statusbar_visible="draft,confirmed,done"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="name" placeholder="Name..."/>
</h1>
</div>
<group>
<group>
<field name="partner_id"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<group>
<field name="total_amount"/>
<field name="active"/>
</group>
</group>
<notebook>
<page string="Lines">
<field name="line_ids">
<tree editable="bottom">
<field name="name"/>
<field name="amount"/>
</tree>
</field>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</form>
</field>
</record>
<!-- Search View -->
<record id="view_model_search" model="ir.ui.view">
<field name="name">module.model.search</field>
<field name="model">module.model</field>
<field name="arch" type="xml">
<search string="Search Model">
<field name="name"/>
<field name="partner_id"/>
<filter string="Draft" name="draft" domain="[('state', '=', 'draft')]"/>
<filter string="Done" name="done" domain="[('state', '=', 'done')]"/>
<separator/>
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
<group expand="0" string="Group By">
<filter string="Partner" name="partner" context="{'group_by': 'partner_id'}"/>
<filter string="Status" name="state" context="{'group_by': 'state'}"/>
</group>
</search>
</field>
</record>
<!-- Action -->
<record id="action_model" model="ir.actions.act_window">
<field name="name">Model Name</field>
<field name="res_model">module.model</field>
<field name="view_mode">tree,form</field>
<field name="context">{}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first record!
</p>
<p>
Click the create button to add a new record.
</p>
</field>
</record>
</odoo>
Step 7: Generate Security Files
Create security groups (if needed):
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="module_category" model="ir.module.category">
<field name="name">Module Category</field>
<field name="sequence">100</field>
</record>
<record id="group_user" model="res.groups">
<field name="name">User</field>
<field name="category_id" ref="module_category"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="group_manager" model="res.groups">
<field name="name">Manager</field>
<field name="category_id" ref="module_category"/>
<field name="implied_ids" eval="[(4, ref('group_user'))]"/>
</record>
</odoo>
Create access rights CSV:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_model_user,module.model.user,model_module_model,group_user,1,1,1,0 access_model_manager,module.model.manager,model_module_model,group_manager,1,1,1,1
Step 8: Generate Tests (Recommended)
Use the odoo-test-creator skill to create comprehensive test suites for the module. The odoo-test-creator skill provides:
- •Test templates for different module types (basic models, constraints, inheritance)
- •Best practices specific to Siafa project standards
- •Solutions to common testing pitfalls (database constraints, HTML fields, permissions)
- •Proper import patterns and setUp methods
To create tests with the odoo-test-creator skill, simply invoke:
Use the odoo-test-creator skill to create tests for [module_name]
The skill will:
- •Analyze the module structure to determine what needs testing
- •Select appropriate test templates based on module type
- •Generate comprehensive test methods for CRUD operations, constraints, computed fields, and business logic
- •Handle database constraints properly (using existing records vs. creating with .sudo())
- •Apply Siafa-specific patterns and best practices
Quick Test Example (if not using the skill):
from odoo.tests.common import TransactionCase
from odoo.exceptions import UserError
class TestModel(TransactionCase):
"""Test cases for module.model"""
def setUp(self):
"""Set up test data"""
super().setUp()
self.Model = self.env['module.model']
# Use existing records when possible
self.partner = self.env['res.partner'].search([], limit=1)
if not self.partner:
self.skipTest("No partner available for testing")
def test_01_create_model(self):
"""Test creating a model record"""
record = self.Model.create({
'name': 'Test Record',
'partner_id': self.partner.id,
})
self.assertTrue(record)
self.assertEqual(record.state, 'draft')
def test_02_constraint_validation(self):
"""Test constraint validation"""
record = self.Model.create({
'name': 'Test Record',
'partner_id': self.partner.id,
})
with self.assertRaises(UserError) as context:
record.write({'invalid_field': 'invalid_value'})
self.assertIn('expected error', str(context.exception))
Important: For production modules, always use the odoo-test-creator skill to ensure comprehensive test coverage and proper handling of Siafa-specific constraints.
Code Standards and Best Practices
Follow these standards when generating module code:
- •
Naming Conventions
- •Module name:
snake_case(e.g.,stock_batch_tracking) - •Model name:
module.model(e.g.,stock.batch.tracking) - •Fields:
snake_case(e.g.,batch_number,expiry_date) - •Methods:
snake_casewith verb prefix (e.g.,action_confirm,_compute_total) - •XML IDs:
view_model_type(e.g.,view_batch_tracking_form)
- •Module name:
- •
Import Order
python# Standard library import logging from datetime import datetime # Odoo imports from odoo import models, fields, api, _ from odoo.exceptions import UserError, ValidationError from odoo.tools import float_compare, float_is_zero
- •
Field Attributes
- •Always provide
stringparameter - •Add
helptext for complex fields - •Use
tracking=Truefor important fields - •Set
index=Truefor searchable fields - •Include
company_idfor multi-company support
- •Always provide
- •
Method Decorators
- •Use
@api.depends()for computed fields - •Use
@api.onchange()for onchange methods - •Use
@api.constrains()for validation - •Use
@api.modelfor class-level methods
- •Use
- •
Error Handling
- •Use
UserErrorfor user-facing errors - •Use
ValidationErrorfor constraint violations - •Always log important actions with
_logger
- •Use
- •
Security
- •Always create access rights CSV
- •Use security groups for sensitive operations
- •Add record rules if row-level security needed
- •Test with different user permissions
Module Type Templates
Reference the assets/templates/ directory for complete templates by module type:
- •
simple_model/- Basic CRUD module - •
extension/- Inheriting existing models - •
pos_custom/- POS customizations - •
stock_enhancement/- Inventory features - •
report_module/- Custom reports
Resources
assets/templates/
Contains complete module templates for different module types. Use these as starting points and customize based on specific requirements.
assets/icon.png
Default module icon. Replace with custom icon if needed (PNG, 128x128px recommended).
assets/index.html
Module description HTML template for the Apps menu.