515 lines
17 KiB
Markdown
515 lines
17 KiB
Markdown
---
|
|
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:
|
|
|
|
1. **Module technical name** (snake_case format, e.g., `stock_batch_tracking`, `pos_custom_receipt`)
|
|
2. **Module display name** (human-readable, e.g., "Stock Batch Tracking", "POS Custom Receipt")
|
|
3. **Module purpose** (1-2 sentence description of functionality)
|
|
4. **Module category** (select from: Sales, Inventory, Accounting, Point of Sale, Human Resources, Manufacturing, Purchases, Warehouse, Website, etc.)
|
|
5. **Dependencies** (base modules required, e.g., `stock`, `account`, `point_of_sale`)
|
|
6. **Module type** (see Module Types section below)
|
|
7. **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:
|
|
|
|
```python
|
|
{
|
|
'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:
|
|
|
|
```python
|
|
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
|
|
<?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
|
|
<?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:
|
|
|
|
```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:
|
|
1. Analyze the module structure to determine what needs testing
|
|
2. Select appropriate test templates based on module type
|
|
3. Generate comprehensive test methods for CRUD operations, constraints, computed fields, and business logic
|
|
4. Handle database constraints properly (using existing records vs. creating with .sudo())
|
|
5. Apply Siafa-specific patterns and best practices
|
|
|
|
**Quick Test Example** (if not using the skill):
|
|
|
|
```python
|
|
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:
|
|
|
|
1. **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_case` with verb prefix (e.g., `action_confirm`, `_compute_total`)
|
|
- XML IDs: `view_model_type` (e.g., `view_batch_tracking_form`)
|
|
|
|
2. **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
|
|
```
|
|
|
|
3. **Field Attributes**
|
|
- Always provide `string` parameter
|
|
- Add `help` text for complex fields
|
|
- Use `tracking=True` for important fields
|
|
- Set `index=True` for searchable fields
|
|
- Include `company_id` for multi-company support
|
|
|
|
4. **Method Decorators**
|
|
- Use `@api.depends()` for computed fields
|
|
- Use `@api.onchange()` for onchange methods
|
|
- Use `@api.constrains()` for validation
|
|
- Use `@api.model` for class-level methods
|
|
|
|
5. **Error Handling**
|
|
- Use `UserError` for user-facing errors
|
|
- Use `ValidationError` for constraint violations
|
|
- Always log important actions with `_logger`
|
|
|
|
6. **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.
|