Como construir módulos Odoo personalizados: um guia do desenvolvedor para OWL, ORM e herança
Quando os 82 módulos oficiais e mais de 40.000 módulos da comunidade do Odoo não atendem exatamente aos seus requisitos de negócios, o desenvolvimento de módulos personalizados preenche a lacuna. Este guia cobre os fundamentos da construção de módulos Odoo em 2026, incluindo estrutura do módulo, estrutura de front-end OWL, padrões ORM, mecanismos de herança e práticas recomendadas alinhadas com as diretrizes da OCA (Odoo Community Association).
O que é um módulo Odoo personalizado?
Um módulo Odoo personalizado é um pacote independente de lógica de back-end Python, definições de visualização XML, componentes de front-end JavaScript e regras de segurança que estendem a funcionalidade do Odoo. Os módulos podem adicionar recursos totalmente novos, modificar o comportamento existente ou integrar o Odoo com sistemas externos. Cada peça do Odoo é um módulo, tornando a arquitetura inerentemente extensível.
Estrutura de diretório do módulo
Um módulo bem organizado segue esta estrutura padrão:
my_custom_module/
├── __init__.py # Python package init
├── __manifest__.py # Module metadata and dependencies
├── models/
│ ├── __init__.py
│ └── my_model.py # Business logic and data models
├── views/
│ ├── my_model_views.xml # Form, tree, and kanban views
│ └── menu.xml # Menu items and actions
├── security/
│ ├── ir.model.access.csv # Access control list
│ └── security.xml # Record rules and groups
├── data/
│ └── data.xml # Default data records
├── static/
│ └── src/
│ ├── js/ # OWL components
│ ├── css/ # Stylesheets
│ └── xml/ # QWeb templates
├── wizard/ # Transient models for wizards
├── reports/ # QWeb report templates
└── tests/ # Unit tests
O arquivo de manifesto
O arquivo __manifest__.py define a identidade do seu módulo:
{
'name': 'My Custom Module',
'version': '18.0.1.0.0',
'category': 'Custom',
'summary': 'Short description of what the module does',
'description': """
Long description with details about
features and configuration.
""",
'author': 'ECOSIRE',
'website': 'https://ecosire.com',
'license': 'LGPL-3',
'depends': ['base', 'sale', 'stock'],
'data': [
'security/ir.model.access.csv',
'views/my_model_views.xml',
'views/menu.xml',
'data/data.xml',
],
'assets': {
'web.assets_backend': [
'my_custom_module/static/src/js/**/*',
'my_custom_module/static/src/css/**/*',
'my_custom_module/static/src/xml/**/*',
],
},
'installable': True,
'application': False,
'auto_install': False,
}
Convenção de versão: {odoo_version}.{major}.{minor}.{patch} (por exemplo, 18.0.1.0.0).
Trabalhando com o ORM
O Mapeamento Objeto-Relacional (ORM) do Odoo é a base de todo o desenvolvimento de back-end. Os modelos são mapeados para tabelas de banco de dados, e o ORM fornece operações CRUD, campos computados, restrições e gerenciamento de fluxo de trabalho.
Definindo um modelo
from odoo import models, fields, api
class ProjectTask(models.Model):
_name = 'my_module.task'
_description = 'Project Task'
_order = 'priority desc, create_date desc'
name = fields.Char(string='Task Name', required=True)
description = fields.Html(string='Description')
state = fields.Selection([
('draft', 'Draft'),
('in_progress', 'In Progress'),
('done', 'Done'),
('cancelled', 'Cancelled'),
], default='draft', tracking=True)
assigned_to = fields.Many2one('res.users', string='Assigned To')
deadline = fields.Date(string='Deadline')
priority = fields.Selection([
('0', 'Normal'),
('1', 'Important'),
('2', 'Urgent'),
], default='0')
tag_ids = fields.Many2many('my_module.tag', string='Tags')
progress = fields.Float(compute='_compute_progress', store=True)
Referência de tipos de campo
| Tipo de campo | Tipo Python | Caso de uso | |---|---|---| | Caráter | | Texto curto (nome, referência) | | Texto | | Texto longo e simples | | HTML | | Conteúdo de texto rico | | Inteiro | interno | Números inteiros | | Flutuador | flutuar | Números decimais | | Booleano | bool | Sinalizadores Verdadeiro/Falso | | Data | data | Data sem hora | | Datahora | datahora | Data com hora | | Seleção | | Opções suspensas | | Muitos2one | interno | Link para um registro | | Um2muitos | lista | Reverso de Many2one | | Muitos2muitos | lista | Link para vários registros | | Binário | bytes | Anexos de arquivo |
Campos e restrições computados
@api.depends('subtask_ids.state')
def _compute_progress(self):
for task in self:
total = len(task.subtask_ids)
done = len(task.subtask_ids.filtered(
lambda t: t.state == 'done'
))
task.progress = (done / total * 100) if total else 0
@api.constrains('deadline')
def _check_deadline(self):
for task in self:
if task.deadline and task.deadline < fields.Date.today():
raise ValidationError(
"Deadline cannot be in the past."
)
Mecanismos de herança
Odoo oferece três tipos de herança, cada um servindo a um propósito diferente:
1. Herança de classe (extensão)
Estenda um modelo existente adicionando campos ou substituindo métodos. Este é o padrão mais comum.
class SaleOrderExtend(models.Model):
_inherit = 'sale.order'
custom_reference = fields.Char(string='Custom Ref')
approved_by = fields.Many2one('res.users')
def action_confirm(self):
# Add custom logic before standard confirmation
for order in self:
if order.amount_total > 10000 and not order.approved_by:
raise UserError("Orders over $10,000 require approval.")
return super().action_confirm()
2. Herança do protótipo
Crie um novo modelo que copie todos os campos e métodos de um modelo existente.
class CustomPartner(models.Model):
_name = 'my_module.partner'
_inherit = 'res.partner' # Copies structure
_description = 'Custom Partner'
3. Herança de delegação
Crie um novo modelo que delegue para um modelo existente por meio de um link Many2one. Os campos do modelo pai aparecem no modelo filho de forma transparente.
class LibraryMember(models.Model):
_name = 'library.member'
_inherits = {'res.partner': 'partner_id'}
partner_id = fields.Many2one('res.partner', required=True,
ondelete='cascade')
membership_date = fields.Date()
member_number = fields.Char()
A estrutura OWL (front-end)
Odoo 18 usa OWL (Odoo Web Library) como estrutura de front-end. OWL é uma estrutura baseada em componentes semelhante ao React ou Vue, mas projetada especificamente para as necessidades do Odoo.
Componente Básico OWL
/** @odoo-module */
import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
class TaskDashboard extends Component {
static template = "my_module.TaskDashboard";
setup() {
this.state = useState({
tasks: [],
filter: 'all',
});
this.loadTasks();
}
async loadTasks() {
this.state.tasks = await this.env.services.orm.searchRead(
"my_module.task",
[["state", "!=", "cancelled"]],
["name", "state", "assigned_to", "deadline"]
);
}
get filteredTasks() {
if (this.state.filter === 'all') return this.state.tasks;
return this.state.tasks.filter(
t => t.state === this.state.filter
);
}
}
Modelo QWeb (XML)
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="my_module.TaskDashboard">
<div class="o_task_dashboard">
<div class="task-filters">
<button t-on-click="() => state.filter = 'all'">All</button>
<button t-on-click="() => state.filter = 'in_progress'">
In Progress
</button>
</div>
<div class="task-list">
<t t-foreach="filteredTasks" t-as="task" t-key="task.id">
<div class="task-card">
<span t-esc="task.name"/>
</div>
</t>
</div>
</div>
</t>
</templates>
Configuração de segurança
Todo modelo precisa de regras de acesso explícitas. Sem eles, nenhum usuário poderá acessar os dados do modelo.
Lista de controle de acesso (ir.model.access.csv)
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_task_user,task.user,model_my_module_task,base.group_user,1,1,1,0
access_task_manager,task.manager,model_my_module_task,my_module.group_manager,1,1,1,1
Regras de registro (security.xml)
<record id="task_own_rule" model="ir.rule">
<field name="name">Own Tasks Only</field>
<field name="model_id" ref="model_my_module_task"/>
<field name="domain_force">
[('assigned_to', '=', user.id)]
</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>
Testando seu módulo
Odoo oferece suporte a testes de unidade Python usando a estrutura unittest com classes de teste específicas do Odoo:
from odoo.tests.common import TransactionCase
class TestTask(TransactionCase):
def setUp(self):
super().setUp()
self.task = self.env['my_module.task'].create({
'name': 'Test Task',
'state': 'draft',
})
def test_task_creation(self):
self.assertEqual(self.task.state, 'draft')
self.assertEqual(self.task.progress, 0)
def test_deadline_constraint(self):
with self.assertRaises(ValidationError):
self.task.write({
'deadline': fields.Date.subtract(
fields.Date.today(), days=1
),
})
Execute testes com: odoo-bin -d test_db --test-enable -i my_custom_module --stop-after-init
Perguntas frequentes
P: Quanto tempo leva para construir um módulo Odoo personalizado? Módulos simples (novos campos, visualizações básicas) levam de 1 a 3 dias. Módulos moderados (novos modelos, fluxos de trabalho, relatórios) levam de 1 a 3 semanas. Módulos complexos (sistemas multimodelos, integrações externas, componentes OWL personalizados) levam de 4 a 12 semanas. Para empresas que precisam de módulos personalizados, mas não possuem desenvolvedores Odoo internos, contrate um desenvolvedor Odoo experiente de nossa equipe.
P: Devo modificar o código principal do Odoo ou criar um módulo separado? Sempre crie um módulo separado. A modificação do código principal prejudica a capacidade de atualização e cria conflitos de mesclagem durante as atualizações de versão. Use herança para estender modelos e visualizações existentes do seu módulo personalizado.
P: Quais são as diretrizes do OCA? A Odoo Community Association (OCA) publica padrões de codificação para qualidade de módulo, incluindo convenções de nomenclatura, requisitos de documentação, expectativas de cobertura de teste e regras de estilo de código. Seguir as diretrizes da OCA garante que seu módulo seja sustentável e compatível com o ecossistema mais amplo da comunidade.
Obtendo ajuda profissional
A construção de módulos Odoo personalizados requer conhecimento em Python, JavaScript, PostgreSQL e convenções de estrutura do Odoo. Se você precisa de uma extensão de campo simples ou de um sistema complexo de vários módulos, os serviços de personalização Odoo da ECOSIRE fornecem módulos prontos para produção construídos de acordo com os padrões OCA.
Entre em contato conosco para discutir os requisitos do seu módulo personalizado e obter uma estimativa de desenvolvimento.
Escrito por
ECOSIRE Research and Development Team
Construindo produtos digitais de nível empresarial na ECOSIRE. Compartilhando insights sobre integrações Odoo, automação de e-commerce e soluções de negócios com IA.
Artigos Relacionados
Integração Amazon.de Odoo: vendendo no maior mercado da Alemanha com Odoo ERP
Como integrar Amazon.de com Odoo ERP para o mercado alemão. Abrange FBA Alemanha, cumprimento pan-europeu, IVA alemão, conformidade com VerpackG e reconciliação de liquidação.
Entrando no mercado alemão de comércio eletrônico com Odoo: guia passo a passo para vendedores internacionais
Guia completo para vendedores internacionais que entram no mercado alemão de comércio eletrônico. Abrange análise de mercado, requisitos legais, registro de IVA, seleção de mercado e configuração do Odoo ERP para venda a consumidores alemães.
Gerenciando retornos de comércio eletrônico alemão com Odoo: estratégias para mercados de alto retorno
Como lidar com as altas taxas de retorno do comércio eletrônico na Alemanha usando o Odoo ERP. Abrange fluxos de trabalho de processamento de devoluções, análise de código de razão, automação de reabastecimento e políticas específicas de mercado para Zalando, Otto, Amazon.de e Kaufland.