OBS: Espera-se que você já tenha passado pelo curso de Odoo previamente e que este esteja instalado. Caso você ainda não possua o Odoo instalado, basta seguir o tutorial de instalação do link https://www.odoo.com/documentation/8.0/setup/install.html.
A documentação encontra-se disponível em: https://www.odoo.com/documentation/8.0/howtos/backend.html
Módulo: É um agrupamento de código e funções de propósitos semelhantes. É um arquivo único.
Mais informações: http://www.tutorialspoint.com/python/python_modules.htm
Pacote: É um diretório que consiste em módulos, subpacotes, sub-subpacotes e assim por diante. Todos os módulos e subpacotes são reunidos a partir de um arquivo __init__.py.
def hello_world():
print("Hello world!")
def hello_user(user):
print("Hello %s!" % user)
hello.py
import hello
hello.hello_wolrd()
from hello import hello_user
hello_user("Julia")
from hello import *
hello_user("Julia")
hello_world()
outro.py
outro.py
outro.py
def laranja():
print("Eu tenho uma laranja!")
laranja.py
def uva():
print("Eu tenho uma uva!")
uva.py
def pera():
print("Eu tenho uma pêra!")
pera.py
Frutas/
├── __init__.py
├── laranja.py
├── pera.py
└── uva.py
from laranja import laranja
from uva import uva
from pera import pera
__init__.py
import Frutas
Frutas.laranja()
Frutas.uva()
Frutas.pera()
outro.py
[options]
...
addons_path = /usr/lib/python2.7/dist-packages/openerp/addons,/path/para/seus/modulos
openerp-server.conf
$ odoo.py scaffold <nome_do_seu_modulo> <path_do_seu_modulo>
bash / cmd
$ odoo.py
bash / cmd
$ cp -r /path/para/seus/modulos/seu_modulo /path/do/odoo/seu_modulo
bash / cmd
$ odoo.py --addons-path=/path/para/seus/modulos
bash / cmd
modulo/
├── __init__.py
├── __openerp__.py
├── models.py
├── controllers.py
├── security
│ └── ir.model.access.csv
|
├── demo.xml
└── templates.xml
models.py, controllers.py, templates.xml: MVC
__init__.py: Pacote
__openerp__.py: Manifesto
O manifesto é um arquivo obrigatório que traz metadados sobre o seu módulo Odoo.
{
'name': "Um Modulo",
'version': '1.0',
'depends': ['base'],
'author': "Nome do Autor",
'category': 'Categoria',
'description': """
Texto da descrição
""",
# arquivos de dados sempre carregados na instalação
'data': [
'meumodulo_view.xml',
],
# arquivos de dados contendo dados de demonstração optativos
'demo': [
'demo_data.xml',
],
}
__openerp__.py
$ odoo.py scaffold openacademy /home/<user>/odoo_course/
bash / cmd
# -*- coding: utf-8 -*-
{
'name': "Open Academy",
'summary': """Gerenciamento de cursos""",
'description': """
Módulo Open Academy para gerenciar treinamentos:
- cursos
- sessões
- registro de participantes
""",
'author': "eSolvere",
'website': "https://www.facebook.com/eSolvere",
'category': 'Test',
'version': '0.1',
'depends': ['base'],
'data': [
'templates.xml',
],
'demo': [
'demo.xml',
],
}
__openerp__.py
Pronto, seu novo módulo já deve aparecer na lista de Módulos locais!
ORM (Object Relational Mapper) é uma técnica de mapeamento de objeto relacional que permite fazer uma relação dos objetos com os dados que os mesmos representam.
=
É uma forma de traduzir código para SQL. No caso, vamos traduzir objetos Python para elementos e comandos SQL.
Mais informações: https://www.odoo.com/documentation/8.0/reference/orm.html
No Odoo, o objeto Model é a representação de uma tabela do banco de dados e consiste de alguns atributos e um ou mais Fields (campos da tabela).
from openerp import models, fields
class ModelMinimo(models.Model):
_name = 'test.model'
name = fields.Char()
Mais informações: https://www.odoo.com/documentation/8.0/reference/orm.html
# -*- coding: utf-8 -*-
from openerp import models, fields, api
class Curso(models.Model):
_name = 'openacademy.curso'
nome = fields.Char(string="Titulo", required=True)
descricao = fields.Text()
id_responsavel = fields.Many2one('res.users',
ondelete='set null', string="Responsável", index=True)
ids_sessoes = fields.One2many(
'openacademy.sessao', 'id_curso', string="Seções")
models.py
## após definir o Model do Curso
class Sessao(models.Model):
_name = 'openacademy.sessao'
nome = fields.Char(required=True)
data_inicio = fields.Date()
duracao = fields.Float(digits=(6, 2), help="Duração em dias")
vagas = fields.Integer(string="Número de vagas")
id_instrutor = fields.Many2one('res.partner', string="Instrutor")
id_curso = fields.Many2one('openacademy.curso',
ondelete='cascade', string="Curso", required=True)
ids_participantes = fields.Many2many('res.partner', string="Participantes")
XML (EXtensible Markup Language) é uma forma de linguagem de marcação criada para armazenar e transportar informações.
<!-- os elementos raíz dos arquivos de dados -->
<openerp>
<data>
... operações ...
</data>
</openerp>
exemplo.xml
Ações são códigos XML contendo interações do usuário com o módulo no decorrer da sua execução. Geralmente, são ativadas por um clique em elementos ou botões.
<record model="ir.actions.act_window" id="action_listar_ideias">
<field name="name">Ideias</field>
<field name="res_model">ideia.ideia</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_ideias" parent="menu_root" name="Ideias" sequence="10"
action="action_listar_ideias"/>
menu.xml
Mais informações: https://www.odoo.com/documentation/8.0/reference/actions.html
Views são códigos XML que podem ser formatados para exibir estruturas da página e permitir a interação de usuários juntamente com as ações. Por exemplo, views de formulários, tabelas, etc.
<record model="ir.ui.view" id="view_id">
<field name="name">view.name</field>
<field name="model">object_name</field>
<field name="priority" eval="16"/>
<field name="arch" type="xml">
<!-- conteudo da view: <form>, <tree>, <graph>, ... -->
</field>
</record>
view.xml
Mais informações: https://www.odoo.com/documentation/8.0/reference/views.html
...
'data': [
'templates.xml',
'openacademy.xml',
],
...
__openerp__.py
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record model="ir.actions.act_window" id="action_listar_cursos">
<field name="name">Cursos</field>
<field name="res_model">openacademy.curso</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">Crie o primeiro curso</p>
</field>
</record>
<menuitem id="openacademy_menu_principal" name="Open Academy"/>
<menuitem id="openacademy_menu" name="Open Academy"
parent="openacademy_menu_principal"/>
<menuitem id="cursos_menu" name="Cursos" parent="openacademy_menu"
action="action_listar_cursos"/>
</data>
</openerp>
openacademy.xml
<record model="ir.ui.view" id="view_form_curso">
<field name="name">curso.form</field>
<field name="model">openacademy.curso</field>
<field name="arch" type="xml">
<form string="Formulário do Curso">
<sheet>
<group>
<field name="nome"/>
<field name="id_responsavel"/>
</group>
<notebook>
<page string="Descrição">
<field name="descricao"/>
</page>
<page string="Sessões">
<field name="ids_sessoes">
<tree string="Sessões Registradas">
<field name="nome"/>
<field name="id_instrutor"/>
</tree>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<!-- antes da action -->
openacademy.xml
<record model="ir.ui.view" id="view_busca_curso">
<field name="name">curso.search</field>
<field name="model">openacademy.curso</field>
<field name="arch" type="xml">
<search>
<field name="nome"/>
<field name="descricao"/>
</search>
</field>
</record>
<!-- antes da action -->
openacademy.xml
<!-- sobrescreve a lista gerada automaticamente para visualização de cursos -->
<record model="ir.ui.view" id="view_lista_curso">
<field name="name">curso.tree</field>
<field name="model">openacademy.curso</field>
<field name="arch" type="xml">
<tree string="Listagem de Curso">
<field name="nome"/>
<field name="id_responsavel"/>
</tree>
</field>
</record>
<!-- antes da action -->
openacademy.xml
<!-- após o menu de cursos -->
<record model="ir.actions.act_window" id="action_listar_sessoes">
<field name="name">Sessões</field>
<field name="res_model">openacademy.sessao</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="sessao_menu" name="Sessões"
parent="openacademy_menu"
action="action_listar_sessoes"/>
openacademy.xml
<!-- após o menu de cursos -->
<record model="ir.ui.view" id="view_form_sessao">
<field name="name">sessao.form</field>
<field name="model">openacademy.sessao</field>
<field name="arch" type="xml">
<form string="Formulário da Sessão">
<sheet>
<group>
<group string="Geral">
<field name="id_curso"/>
<field name="nome"/>
<field name="id_instrutor"/>
</group>
<group string="Agendamento">
<field name="data_inicio"/>
<field name="duracao"/>
<field name="vagas"/>
</group>
<label for="ids_participantes"/>
<field name="ids_participantes"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- antes do menu de sessões -->
openacademy.xml
<record model="ir.ui.view" id="view_lista_sessao">
<field name="name">sessao.tree</field>
<field name="model">openacademy.sessao</field>
<field name="arch" type="xml">
<tree string="Listagem de Sessão">
<field name="nome"/>
<field name="id_curso"/>
</tree>
</field>
</record>
<!-- antes do menu de sessões -->
openacademy.xml
Herança é um termo utilizado para demonstrar que estamos pegando um objeto já existente e modificando ele. No Odoo, tanto views quanto models podem herdar outras views e models.
Mais informações: https://www.odoo.com/documentation/8.0/howtos/backend.html#inheritance
# -*- coding: utf-8 -*-
from openerp import fields, models
class Partner(models.Model):
_inherit = 'res.partner'
# adicionaremos mais um campo ao model Partner
# por padrão o participante não é instrutor
instrutor = fields.Boolean("Instrutor", default=False)
ids_sessoes = fields.Many2many('openacademi.sessao',
string="Sessões que participou", readonly=True)
participante.py
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<!-- Adicionaremos o campo de instrutor para a view existente -->
<record model="ir.ui.view" id="view_form_participante_instrutor">
<field name="name">partner.instructor</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Sessões">
<group>
<field name="instrutor"/>
<field name="ids_sessoes"/>
</group>
</page>
</notebook>
</field>
</record>
</data>
</openerp>
participante.xml
<!-- depois da view -->
<record model="ir.actions.act_window" id="action_listar_contatos">
<field name="name">Contatos</field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="configuracoes_menu" name="Configurações"
parent="openacademy_menu_principal"/>
<menuitem id="contatos_menu" name="Contatos"
parent="configuracoes_menu"
action="action_listar_contatos"/>
participante.xml
# -*- coding: utf-8 -*-
import controllers
import models
import participante
__init__.py
...
'data': [
'templates.xml',
'openacademy.xml',
'participante.xml'
],
...
__openerp__.py
No Odoo, domínios são valores que aplicam condições à registros. Um domínio é uma lista de critérios utilizados para selecionar uma parcela de registros no banco de dados. Cada critério é uma tupla com o nome de um campo, um operador e um valor.
Por padrão, critérios são combinados com um operador AND implícito. Porém, os operadores lógicos & (and), | (or) e ! (not) podem ser usados explicitamente para combinar os critérios. Eles são utilizados antes dos domínios, como um prefixo.
## Em um módulo que trabalha com produtos, por exemplo
[('product_type', '=', 'service'), ('unit_price', '>', 1000)]
## Equivale à "Selecione todos os produtos que sejam serviços E
## o preço unitário seja maior que 1000"
['|',
('product_type', '=', 'service'),
'!', '&',
('unit_price', '>=', 1000),
('unit_price', '<', 2000)
]
## Equivale a "Selecione os registros que são serviços OU
## que tenham o preço unitário que NÃO seja maior ou igual
## a 1000 E menor que 2000"
Mais informações: https://www.odoo.com/documentation/8.0/howtos/backend.html#domains
## após definir o Model do Curso
class Sessao(models.Model):
_name = 'openacademy.sessao'
nome = fields.Char(required=True)
data_inicio = fields.Date()
duracao = fields.Float(digits=(6, 2), help="Duração em dias")
vagas = fields.Integer(string="Número de vagas")
id_instrutor = fields.Many2one('res.partner', string="Instrutor",
domain=[('instrutor', '=', True)])
id_curso = fields.Many2one('openacademy.curso',
ondelete='cascade', string="Curso", required=True)
ids_participantes = fields.Many2many('res.partner', string="Participantes")
models.py
Workflows são models associados a objetos que descrevem suas dinâmicas. Além disso, podem ser utilizados para monitorar processos que evoluem com o tempo.
Por exemplo, nossas sessões podem ter três estados: Planejado, Confirmado e Realizado. Um workflow faria com que um campo do nosso model trabalhasse com esses estados.
Mais informações: https://www.odoo.com/documentation/8.0/reference/workflows.html
Um decorator Python é uma forma prática e reusável de adicionarmos funcionalidades às nossas funções/métodos/classes, sem precisarmos alterar o código delas.
No Odoo, por exemplo, utilizar o decorator @api.multi nos nossos models é o que nos possibilita alterar o nosso registro por meio de um método do model.
class SeuModel(model.Model):
_name = "seumodulo.seumodel"
nome = fields.Char()
@api.multi
def nome_joao(self):
self.nome = "João"
## no model de Sessões adicionar:
...
state = fields.Selection([
('planejado', "Planejado"),
('confirmado', "Confirmado"),
('realizado', "Realizado")
], default='planejado')
@api.multi
def action_planejado(self):
self.state = 'planejado'
@api.multi
def action_confirmado(self):
self.state = 'confirmado'
@api.multi
def action_realizado(self):
self.state = 'realizado'
models.py
<!-- Formulário de Sessão -->
<form string="Formulário da Sessão">
<header>
<button name="action_planejado" type="object"
string="Resetar para planejamento"
states="confirmado,realizado"/>
<button name="action_confirmado" type="object"
string="Confirmar" states="planejado"
class="oe_highlight"/>
<button name="action_realizado" type="object"
string="Marcar como realizado" states="confirmado"
class="oe_highlight"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
...
<!-- continuação do formulário -->
openacademy.py
Para manter nossos módulos seguros, precisamos utilizar mecanismos de controle de acesso. Esses mecanismos irão garantir que apenas usuários autorizados irão ter controle das funções do sistema.
O Odoo utiliza um mecanismo de controle de acesso baseado em grupos, no qual grupos de usuários são criados no model res.groups e são autorizados a acessar as funcionalidades do menu a partir das definições do próprio menu. Além disso, algumas operações podem ser acessadas sem a necessidade do menu, por isso permissões a nível de objeto também devem ser dadas aos grupos por meio de arquivos XML.
Mais informações: https://www.odoo.com/documentation/8.0/reference/security.html
CSV (Comma-separated Values) é um arquivo de texto utilizado para armazenar dados. O arquivo é definido por meio de um sistema de colunas separadas por vírgulas e linhas separadas pela quebra de linha, como se fosse uma tabela.
Nome,Profissao,Fone
João Silva,Professor,(34)99999-9999
Maria Silva,Empresária,(34)98888-8888
José Santos,Estagiário,(34)98989-8989
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
admin_curso,Admin do Curso,model_openacademy_curso,grupo_admin,1,1,1,1
admin_sessao,Admin da Sessao,model_openacademy_sessao,grupo_admin,1,1,1,1
ver_curso_todos,Todos do Curso,model_openacademy_curso,,1,0,0,0
ver_sessao_todos,Todos da Sessão,model_openacademy_sessao,,1,0,0,0
security/ir.model.access.csv
<openerp>
<data>
<record id="grupo_admin" model="res.groups">
<field name="name">OpenAcademy / Admin</field>
</record>
</data>
</openerp>
security/security.xml
'data': [
'security/security.xml',
'security/ir.model.access.csv',
'templates.xml',
'openacademy.xml',
'participantes.xml'
],
__openerp__.py