--- name: odoo-18-oca-architect description: > Enforce OCA patterns and Odoo 18 CE best practices. Use when building or reviewing Odoo modules to ensure compliance with Community Edition and OCA guidelines. tags: - odoo - oca - architecture - modules - python agent_hint: coding-only version: 1.0.0 --- # Skill: Odoo 18 CE/OCA Architect You are a senior Odoo architect specializing in Community Edition and OCA-compliant module development. ## Purpose Use this skill to: - Build new Odoo 18 CE modules that follow OCA guidelines - Review existing modules for OCA compliance - Refactor Enterprise-dependent code to CE alternatives - Ensure proper module structure, security, and testing --- ## Core Principles ### 1. Config → OCA → Delta Philosophy Always prefer: 1. **Configuration** — Use Odoo's built-in config before writing code 2. **OCA Module** — Check if an OCA module already does what you need 3. **Custom Delta** — Only then write minimal custom code ### 2. CE-Only Constraint **NEVER** introduce: - Odoo Enterprise modules - IAP (In-App Purchase) dependencies - Studio customizations - Enterprise-only fields or features ### 3. OCA Naming Conventions - Module names: `l10n_ph_*` for localizations, `account_*` for accounting - Custom modules: `ipai_*` or company prefix - Version format: `18.0.x.y.z` (Odoo major.minor.patch.module) --- ## Module Structure Template ``` addons/ └── ipai_module_name/ ├── __manifest__.py # Module metadata ├── __init__.py # Package init ├── models/ │ ├── __init__.py │ └── my_model.py # Model definitions ├── views/ │ └── my_model_views.xml ├── security/ │ ├── ir.model.access.csv │ └── security_rules.xml ├── data/ │ └── initial_data.xml ├── wizard/ # Transient models ├── report/ # QWeb reports ├── static/ │ └── description/ │ └── icon.png # 128x128 module icon ├── tests/ │ ├── __init__.py │ └── test_my_model.py ├── README.rst # Module documentation └── LICENSE # AGPL-3 or LGPL-3 ``` --- ## Manifest Template ```python { "name": "Module Display Name", "version": "18.0.1.0.0", "category": "Accounting/Localizations", "summary": "Brief one-line description", "description": """ Long description with: - Features list - Usage instructions - Credits """, "author": "Your Company, OCA", "website": "https://github.com/OCA", "license": "AGPL-3", "depends": [ "account", "base", ], "data": [ "security/ir.model.access.csv", "views/my_model_views.xml", "data/initial_data.xml", ], "demo": [ "demo/demo_data.xml", ], "installable": True, "application": False, "auto_install": False, } ``` --- ## Model Patterns ### Standard Model ```python from odoo import api, fields, models, _ from odoo.exceptions import UserError, ValidationError class MyModel(models.Model): _name = "my.model" _description = "My Model Description" _order = "date desc, id desc" _inherit = ["mail.thread", "mail.activity.mixin"] # Optional # Fields name = fields.Char( string="Name", required=True, tracking=True, ) date = fields.Date( string="Date", default=fields.Date.context_today, ) state = fields.Selection( selection=[ ("draft", "Draft"), ("confirmed", "Confirmed"), ("done", "Done"), ], string="Status", default="draft", tracking=True, ) company_id = fields.Many2one( comodel_name="res.company", string="Company", required=True, default=lambda self: self.env.company, ) # Computed fields @api.depends("line_ids.amount") def _compute_total(self): for record in self: record.total = sum(record.line_ids.mapped("amount")) total = fields.Monetary( string="Total", compute="_compute_total", store=True, ) # Constraints @api.constrains("date") def _check_date(self): for record in self: if record.date > fields.Date.today(): raise ValidationError(_("Date cannot be in the future.")) # Actions def action_confirm(self): self.write({"state": "confirmed"}) return True ``` ### Inheritance Patterns ```python # Extending existing model (add fields) class AccountMove(models.Model): _inherit = "account.move" custom_field = fields.Char(string="Custom Field") # Delegation inheritance (embed model) class ProductProduct(models.Model): _name = "product.product" _inherits = {"product.template": "product_tmpl_id"} # Abstract model (mixin) class MyMixin(models.AbstractModel): _name = "my.mixin" _description = "Reusable Mixin" shared_field = fields.Char() ``` --- ## Security Patterns ### ir.model.access.csv ```csv id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_my_model_user,my.model.user,model_my_model,base.group_user,1,0,0,0 access_my_model_manager,my.model.manager,model_my_model,account.group_account_manager,1,1,1,1 ``` ### Record Rules (Multi-Company) ```xml My Model: Multi-Company ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] ``` --- ## Testing Patterns ```python from odoo.tests.common import TransactionCase, tagged from odoo.exceptions import ValidationError @tagged("post_install", "-at_install") class TestMyModel(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.Model = cls.env["my.model"] cls.record = cls.Model.create({ "name": "Test Record", "date": "2025-01-01", }) def test_01_create(self): """Test record creation.""" self.assertEqual(self.record.state, "draft") self.assertEqual(self.record.name, "Test Record") def test_02_confirm(self): """Test confirmation workflow.""" self.record.action_confirm() self.assertEqual(self.record.state, "confirmed") def test_03_validation(self): """Test date validation constraint.""" with self.assertRaises(ValidationError): self.Model.create({ "name": "Future Record", "date": "2099-12-31", }) ``` --- ## View Patterns ### Form View ```xml my.model.form my.model
``` ### Tree View ```xml my.model.tree my.model ``` --- ## Checklist for OCA Compliance Before submitting any Odoo module: - [ ] `__manifest__.py` has correct version format (18.0.x.y.z) - [ ] License is AGPL-3 or LGPL-3 - [ ] No Enterprise module dependencies - [ ] All strings use `_()` for translation - [ ] Security files present (ir.model.access.csv) - [ ] Multi-company support if applicable - [ ] Tests exist with meaningful coverage - [ ] README.rst documents features and usage - [ ] No hardcoded IDs (use XML IDs) - [ ] PEP8 / Black formatting - [ ] No direct SQL unless absolutely necessary --- ## Examples Use this skill when the user asks: - "Create a new Odoo 18 module for tax form tracking." - "Is this module OCA-compliant?" - "Refactor this Enterprise feature to work with CE." - "Add multi-company support to this module." - "Review this module's security setup."