Claude Code Plugins

Community-maintained marketplace

Feedback

odoo-18-oca-architect

@jgtolentino/TaxPulse-PH-Pack
0
0

>

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

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

{
    "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

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

# 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

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)

<record id="my_model_company_rule" model="ir.rule">
    <field name="name">My Model: Multi-Company</field>
    <field name="model_id" ref="model_my_model"/>
    <field name="domain_force">
        ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]
    </field>
</record>

Testing Patterns

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

<record id="my_model_view_form" model="ir.ui.view">
    <field name="name">my.model.form</field>
    <field name="model">my.model</field>
    <field name="arch" type="xml">
        <form string="My Model">
            <header>
                <button name="action_confirm"
                        type="object"
                        string="Confirm"
                        class="btn-primary"
                        invisible="state != 'draft'"/>
                <field name="state" widget="statusbar"/>
            </header>
            <sheet>
                <group>
                    <group>
                        <field name="name"/>
                        <field name="date"/>
                    </group>
                    <group>
                        <field name="total"/>
                        <field name="company_id"/>
                    </group>
                </group>
            </sheet>
            <div class="oe_chatter">
                <field name="message_follower_ids"/>
                <field name="activity_ids"/>
                <field name="message_ids"/>
            </div>
        </form>
    </field>
</record>

Tree View

<record id="my_model_view_tree" model="ir.ui.view">
    <field name="name">my.model.tree</field>
    <field name="model">my.model</field>
    <field name="arch" type="xml">
        <tree decoration-info="state == 'draft'"
              decoration-success="state == 'done'">
            <field name="name"/>
            <field name="date"/>
            <field name="total"/>
            <field name="state"/>
        </tree>
    </field>
</record>

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."