Python para Módulos Odoo

Júlia Rizza

contato@juliarizza.com

Objetivos

  • Aprender conceitos avançados de Python
  • Compreender a estrutura do Odoo
  • Criar módulos para o Odoo

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ódulos Python x Módulos Odoo

  • Traz funcionalidades
  • Agrupamento
  • Sub-grupo do empacotamento
  • Traz aplicações
  • É um pacote Python
  • Composto por diversos subdiretórios
  • Possui um manifesto

Módulos e Pacotes Python

Módulo: É um agrupamento de código e funções de propósitos semelhantes. É um arquivo único.

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.

Módulo

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

Pacote

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

iniciando um módulo Odoo

[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

CRIAR

Executar

OU

$ cp -r /path/para/seus/modulos/seu_modulo /path/do/odoo/seu_modulo
bash / cmd

E então

OU

$ odoo.py --addons-path=/path/para/seus/modulos
bash / cmd

Módulo Odoo

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

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

Modelo MVC

  • Model: configurações e banco de dados

 

  • Controller: funções, páginas, lógica da aplicação

 

  • View: apresentação de informações

1. criar o módulo

$ odoo.py scaffold openacademy /home/<user>/odoo_course/
bash / cmd

2. Editar o manifesto

# -*- 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

instalando o novo módulo

  1. Acessar a URL 0.0.0.0:8069
  2. Selecionar Manage Databases
  3. Criar um novo banco de dados (sugestão de senhas: admin)
  4. Logar com o banco de dados criado
  5. Se em uma versão <8:
    1. No menu Usuários editar o usuário Administrador e permitir Procedimentos Técnicos
    2. Clicar em Atualizar lista de módulos

Pronto, seu novo módulo já deve aparecer na lista de Módulos locais!

orm

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.

construindo models

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()

3. Criar tabelas

# -*- 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

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

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

Views

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

4. Criar um menu para cursos

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

5. criar o formulário de cursos

<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

6. Alterar busca de cursos

<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

7. Alterar a exibição dos cursos

<!-- 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

8. criar um menu para as sessões

<!-- 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

9. criar formulário de sessões

<!-- 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

10. alterar a exibição de sessões

<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

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.

 

  • Herança de Views: são criadas views que extendem uma pré-existente a partir do field XML inherit_id.
  • Herança de Models: é criada uma classe que extende um Model pré-existente a partir do atributo _inherit.

11. Editar model de participantes

# -*- 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

12. Editar view de participantes

<?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

13. Criar menu de participantes

<!-- 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

14. adicionar arquivos na lista

# -*- coding: utf-8 -*-
import controllers
import models
import participante
__init__.py
...
'data': [
        'templates.xml',
        'openacademy.xml',
        'participante.xml'
        ],
...
__openerp__.py

Domínios

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"

15. Criar domínio para instrutor

## 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

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.

Decorators

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"

16. Criar um workflow de estados

## 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

16. Criar um workflow de estados

<!-- 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

Segurança

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.

CSV

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

17. modificar permissões

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

pronto!

O que mais eu posso fazer no meu módulo?

Python para Módulos Odoo

Júlia Rizza

contato@juliarizza.com

Python para Odoo - eSolvere

By Júlia Rizza

Python para Odoo - eSolvere

  • 1,534