| name | odoo-module-creator |
| description | Creates complete Odoo 16.0 modules with proper structure, manifests, models, views, and security. This skill should be used when the user requests creation of a new Odoo module, such as "Create a new module for inventory tracking" or "I need a new POS customization module" or "Generate module structure for vendor management". |
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
# 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_zeroField 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 moduleextension/- Inheriting existing modelspos_custom/- POS customizationsstock_enhancement/- Inventory featuresreport_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.