Développement de modules pour Odoo:
exemples et exercices corrigés
Abdelouahed Sabri
Université Sidi Mohamed Ben Abdellah
Faculté des Sciences Dhar El Mahraz Fès
Plan
Introduction
Structure d’un module Odoo
Objets (tables) Odoo
Les types de champs d’un objets
Les interfaces
Les menus
Les actions
Les vues (views)
Internationalisation
Introduction
Odoo utilise une architecture trois tiers qui est une architecture client-serveur. On trouve 3 couches :
1- couche présentation (interface utilisateur),
2- couche métier (business ou aussi fonctionnelle) responsable de la miseen œuvre de l’ensemble des règles de gestion et de la logique applicativeet
3- couche d’accès aux données
Ces 3 couches sont développées et maintenues sous forme demodules indépendants
Le noyau d’Odoo et de ses différents modules sont écrits en Python. La fonctionnalité d’un module est gérée par le protocole XML-RPC
(et/ou NET-RPC)
Les modules en Odoo sont aussi appelés extensions ou plugins (addons) et sont open-source.
Introduction
ORM OpenERP
Les modules Odoo font usage de l’ORM (Object-Relational
Mapping) de Odoo pour rendre persistantes leurs données
dans une base de données relationnelle (PostgreSQL).
Les données peuvent être insérées en utilisant le module ou lors de
l’installation utilisant des fichiers XML, CSV ouYML
On peut écrire (développer) de nouveaux modules on
utilisant Python, des fichiers XML et utiliser l’ORM de
Odoo
Modules Odoo Tout les modules sont localisés dans le dossier
«\server\openerp\addons »
Un module Odoo doit vérifier les conditions suivantes : Le nom du dossier contenant les fichiers du module doit être le même
que le nom du module lui même
Trois fichiers Python sont obligatoires : __init__.py : fichier d’initialisation du module
__openerp__.py : fichier de description du module
NomDuModule.py : fichier des objets
Les vues (formulaires, listes, ...), les menus et les actions sont définiesdans un Fichier XML
Il est possible d’ajouter des données de démonstration lors del’installation. Ces données sont dans un fichier XML.
Il est possible de créer des rapports, des Wizard (assistants) et/oudes Workflow (flux de travail)
NB: L’encodage des fichiers doit être en format UTF8
Modules Odoo
Le fichier d’initialisation du module __init__.py
Comme pour tous les modules Python, ce fichier est exécuté
au début du programme.
Il a besoin d'importer les fichiers Python qui doivent être
chargés en mémoire.
NB:
Si vous créez un fichier "NomDuModule.py" contenant la
description des objets du module, il faut écrire dans le fichier
__init__.py la ligne suivante :
import NomduModule
Modules Odoo
Le fichier de description du module __openerp__.py
C’est un fichier Python permettant la description du module et
aussi responsable de :
Spécifier les fichiers XML qui seront analysés (parsed) lors de
l'initialisation du serveur,
Spécifier les dépendances du module créé
Modules Odoo
Le fichier de description du module __openerp__.py
Ce fichier doit contenir un dictionnaire Pyhton avec les
valeurs suivantes :name Le nom du module
version La version du module, sur deux chiffres (exp. 1.1 ou 2.3)
description La description du module y compris la documentation sur l'utilisation du
module
author L'auteur du module
website Le site Web du module
licence La licence du module (par défaut: GPL-2).
installable True ou False. Indique si le module est installable ou non
active True ou False (False défaut).
category La catégorie dans laquelle le module va être placée
application True ou False. Pour que le module soit identifié comme application. Seul
Odoo délivre les certificats qualifiant un module d’application.
data Liste de fichiers xml contenant les interfaces
Modules Odoo
Exemple
Créer un dossier (module) vide « gestion_sigl» dans le
répertoire «\server\openerp\addons »
Créer les deux fichiers Python (obligatoire) :
__init__.py et __openerp__.py
Redémarrer le serveur ou bien dans le menu d’Odoo
« Configuration » « Modules » « Mettre à jour la liste
des modules »
Chercher le module
Objets (tables) Odoo
Le fichier NomduModule.py
Ce fichier va contenir les classes (objets Odoo) de la couche
model permettant de persister les données dans la base de
données via la couche ORM de Odoo
Les objets métiers sont déclarés comme des classes Python
héritant de la classe osv.osv qui utilise le Framework
ObjectSerVice (OSV).
Objets (tables) Odoo
Objets
Pour définir un nouvel objet, il faut définir une nouvelle classe
Python puis l’instancier. Cette classe doit hériter de la classe
osv du module osv.
Le canevas est le suivant:
# -*-coding: utf-8 -*-
from openerp.osv import fields, osv
class nom_de_l_objet(osv.osv):
# définition des champs (attributs) de la classe
nom_de_l_objet()
Objets (tables) Odoo
Objets
Un objet est défini par la déclaration dans la classe de quelques champs
(attributs) avec des noms prédéfinis.
Deux attributs sont obligatoires : « _name et _columns », le reste est
optionnel._name (required) Nom de l’objet
_columns (required) Les champs de l’objet
_constraints Contraintes sur l’objet
_table Nom de la table SQL. Par défaut c’est la valeur du champs _name où
les points ( . ) sont remplacés par underscores ( _ )
_sql_constraints Les contraintes SQL
...# -*-coding: utf-8 -*-
from openerp.osv import fields, osv
class nom_de_l_objet(osv.osv):
_name= 'nom.delobjet' # _table= 'nom_delobjet'
_columns= { … }
nom_de_l_objet()
Objets (tables) Odoo
Les types de champs d’un objet
Les objets Python peuvent contenir différents types de champs.
Ces types sont divisés en trois catégories;
1. Simple: ce sont les types simples à savoir boolean, integer, date,
float, char, text, selection, binary..
2. Relationnel : Représente les types de relations entre les objets :
many2one, one2many, many2many
3. Fonctionnel : des champs calculés
NB: Les types de champs sont déclarés dans la classe « fields ».
Objets (tables) Odoo
Les types de champs d’un objet
Les types simples
Syntaxe
boolean fields.boolean('Field Name' [, Optional Parameters])
integer fields.boolean('Field Name' [, Optional Parameters])
float fields.float('Field Name' , digits=(12,6), [, Optional Parameters])
char fields.char('Field Name', size=n [,Optional Parameters])
Text fields.text('Field Name' [, Optional Parameters])
date fields.date('Field Name' [, Optional Parameters])
....
Optionnal Parameters peut être par exemple
required=True pour un champ obligatoire
Objets (tables) Odoo
Les types de champs d’un objet
Les types simples
Exemple:
Un premier exemple d’application est de créer un objet permettantle mapping de la table ELEVE suivante:
ELEVE (numE (char de taille 16), nomE (char de taille 32), nomC (char de taille 32))
# -*-coding: utf-8 -*-
from openerp.osv import fields, osv
class gestion_eleve(osv.osv):
_name= 'gestion.eleve'
_description = u"Table des éléves"
_columns= {
'nume' : fields.char(u'Numéro de l étudiant', size=16, required=True),
'nome': fields.char(u'Nom de l étudiant', size=32, required=True),
'nomc': fields.char(u'Nom de la classe', size=32, required=True)
}
gestion_eleve()
Objets (tables) Odoo
Les types de champs d’un objet
Les types simples
Exercice:
Créer un module pour la gestion d’une école. Le module consiste en
la création des tables suivantes :
gestion.eleve (numE (char de taille 16), nomE (char de taille 32),
nomC (char de taille 32))
gestion.classe (nomC (char de taille 16))
gestion.professeur (numP (char de taille 16), nomP (char de taille
32))
gestion.service (numP (number de taille 9), nomC (char de taille
16), nbh(float de taille 16))
# -*-coding: utf-8 -*-
from openerp.osv import fields, osv
class gestion_eleve(osv.osv):
_name= 'gestion.eleve'
_description = u"Table des éléves"
_columns= {
'nume' : fields.char(u'Numéro de l étudiant', size=16, required=True),
'nome': fields.char(u'Nom de l étudiant', size=32, required=True),
'nomc': fields.char(u'Nom de la classe', size=32, required=True)
}
gestion_eleve()
class gestion_classe(osv.osv):
_name= 'gestion.classe'
_description = u"Table des classe"
_columns= {
'nomc': fields.char(u'Nom de la classe', size=32, required=True)
}
gestion_classe()
class gestion_professeur(osv.osv):
_name= 'gestion.professeur'
_description = u"Table des professeur"
_columns= {
'nump' : fields.char(u'Numéro du prof', size=16, required=True),
'nomp': fields.char(u'Nom de du prof', size=32, required=True),
}
gestion_professeur()
class gestion_service(osv.osv):
_name= 'gestion.service'
_description = u"Table des services"
_columns= {
'nump' : fields.integer(u'Numéro du prof', size=9, required=True),
'nomc': fields.char(u'Nom de la classe', size=32, required=True),
'nbh': fields.float(u'Nombre d heures assurés par un prof', digits=(9,4), required=True)
}
gestion_service()
Objets (tables) Odoo
Les types de champs d’un objet
Les types relationnels
Syntaxe
one2one fields.one2one('other.object.name', 'Field Name')
many2one fields.many2one('other.object.name', 'Field Name', optional
parameters)
one2many fields.one2many('other.object.name', 'Field relation id',
'Fieldname', optional parameter)
Paramètres optionnels:
invisible: True/False
states: ?
readonly: True/False
many2many fields.many2many('other.object.name', 'relation object',
'actual.object.id', 'other.object.id', 'FieldName')
Les interfaces
Le mécanisme principal pour insérer les données est de le faire utilisant les composants d'interface du module.
Pour créer une interface d’un module, il faut créer ; les menus, les vues, les actions, les rôles, les droits d'accès, etc.
Sous Odoo, les interfaces sont crées et configurées utilisant des fichiers XML.
Le fichier contenant la définition des éléments de l’interfacedoit être référencé dans l’attribut « data » du fichier« __openerp__.py ».
Ainsi, si le fichier « projet_view.xml » contient la descriptionde l’interface du projet, dans le fichier il faut ajouter la ligne"data" : ['projet_view.xml']
Les interfaces
Canevas du fichier XML
<?xml version="1.0" ?>
<openerp>
<data>
Déclaration des éléments de l’interface
</data>
</openerp>
Les interfaces Les menus
<?xml version="1.0" ?>
<openerp>
<data>
Déclaration des éléments de l’interface
</data>
</openerp>
Le menu principal est déclaré comme suit :<menuitem id="idMenuPrincipal" name="Nom affiché dans la barre des menus" />
1ér sous menu (non cliquable)<menuitem id="idSousMenu" parent="idMenuPrincipal" name="Nom du menu" />
2éme sous menu (cliquable)<menuitem id="idSousSousMenu" parent="idSousMenu" name="Nom affiché" action="[ACTION_ID]"/>
Où action est l’événement déclenché après clique sur le menu. Ainsi, il faut définir uneaction à exécuter. Il faut qu’elle soit implémentée
Les interfaces
Les menus
Exercice: Ajouter un menu « Gestion d'école » au module « Gestiond’école » déjà crée. Et ajouter trois sous-menus « Etudiants »,« Professeur » et « Classe », « Service » et «Classe ».
On cherche à avoir la hiérarchie suivante :
Gestion d'école
Gestion d'école/Gestion d'ecole
Gestion d'école/Gestion d'ecole/Classes
Gestion d'école/Gestion d'ecole/Etudiants
Gestion d'école/Gestion d'ecole/Professeurs
Gestion d'école/Gestion d'ecole/Service
NB: Pour vérifier les menus crées cliquer « Local Modules / Modulede gestion d'école » et cliquer l’onglet « Installed features » et« Menus crées »
Les interfaces
Les menus
Exercice (suite)
<?xml version="1.0" ?>
<openerp>
<data>
<menuitem id="menuecole" name="Gestion d'école"/>
<menuitem name="Gestion d'ecole" id="main_menu" parent="menuecole"/>
<menuitem name="Etudiants" id="gest_etud" parent="main_menu"/>
<menuitem name="Professeurs" id="gest_prof" parent="main_menu" />
<menuitem name="Classes" id="gest_classe" parent="main_menu"/>
</data>
</openerp>
Les interfaces
Les actions Une action est un événement déclenché suite à un click.
Pour déclarer une action, il faut utiliser le modèle"ir.actions.act_window"
Syntaxe:
<record model="ir.actions.act_window" id="actionId">
<field name="name">NomDAction</field>
<field name="res_model">TableBDD</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
Où : id : identifiant de l’action (unique)
model: type d’élément, ici c’est une action
name : le nom de l’action (obligatoire)
res_model : le model (objet) python sur lequel pointe l’action (obligatoire)
view_mode : lise des vues autorisées.
view_type : type de vue utilisée par défaut
Les interfaces
Les actions
Exemple: Action clique sur le menu Etudiants
<record model="ir.actions.act_window" id="action_etudiants">
<field name="name">Etudiants</field>
<field name="res_model">gestion.eleve</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
</record>
<menuitem name="Etudiants" id="gest_etud"
parent="main_menu" action="action_etudiants"/>
Exercice: Ajouter des actions aux autres menus vus
précédemment (Professeurs, Classe, ....)
Les interfaces
Les vues (views)
Les vues sont un moyen pour représenter les objets sur le
navigateur du client.
Plusieurs vues peuvent être déclarées pour le même objet et
peuvent être utilisées suivant leur priorités.
On peut distinguer plusieurs types de vues :
Formulaire (form views)
Arbre (tree views et list views)
graph
Kanban
Calendar
Gantt
Les interfaces
Les vues (views)
Pour déclarer une vue il faut utiliser le modèle "ir.ui.view"
<record model="ir.ui.view" id="ID_Vue">
<field name="name">Nom.Vue</field>
<field name="model">TableBDD</field>
<field name="type">form</field>
<field name="arch" type="xml">
<!--contenu de la vue: <form>, <tree>, <graph>, … -->
</field>
</record>
Où:
type: type de vue (par défaut form)
arch: l’architecture de la vue et qui est de type XML
Les interfaces
Vue formulaire
<form string="Etudiant">
<field name="nomChamp1"/>
<field name="nomChamp2"/>
</form>
Où
form: pour dire que c’est une vue de type formulaire
field : pour déclarer les champs. Autant de champs que
nécessaire
<?xml version="1.0" ?>
<openerp>
<data>
<record model="ir.ui.view" id="etudFormID">
<field name="name">etudiant.vue</field>
<field name="model">gestion.eleve</field>
<field name="arch" type="xml">
<form string="Etudiant">
<label string="Numero de l'etudiant" /> <field name="nume"/>
<label string="Nom de l'etudiant" /> <field name="nome"/>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="action_etudiants">
<field name="name">Etudiants</field> <field name="res_model">gestion.eleve</field>
<field name="view_type">form</field> <field name="view_mode">form,tree</field>
</record>
<menuitem id="menuecole" name="Gestion d'école"/>
<menuitem name="Gestion d'ecole" id="main_menu" parent="menuecole"/>
<menuitem name="Etudiants" id="gest_etud" parent="main_menu" action="action_etudiants"/>
<menuitem name="Professeurs" id="gest_prof" parent="main_menu" />
<menuitem name="Classes" id="gest_classe" parent="main_menu"/>
</data> </openerp>
Les interfaces
Les attributs du formulaire (form)
Attributs communs:
string: étiquette de l’élément
nolabel: 1 pour masquer l’élément
colspan: : Nombre de colonnes sur laquelle le champ doit s’étendre
rowspan: Nombre de lignes sur laquelle le champ doit s’étendre
col: Nombre de colonnes à allouer à ses éléments enfants
invisible: 1 pour cacher complètement l’élément
…
Les interfaces
Les attributs du formulaire (form)
Attributs de field:
required: “True” un champ obligatoire
readonly:True..Champ en lecture seul
password:True.
on_change: Méthode Python à appeler quand la valeur du champ
change
...
Les interfaces
Les attributs du formulaire (form)
Propriétés des boutons
type: type du bouton: workflow (par défaut), objet, ou action
name: signal workflow, nom d’une fonction (sans parenthèses) ou
action à appeler (dépendant du type)
confirm: message de confirmation si cliqué
states: liste d’états d’affichage du bouton, utilisant un séparateur
comma
Les interfaces
Vue liste (list ou arbre)<record model="ir.ui.view" id="etudTreeID">
<field name="name">etudiant.tree</field>
<field name="model">gestion.eleve</field>
<field name="arch" type="xml">
<tree string="Etudiant" >
<field name="nume"/>
<field name="nome"/>
</tree>
</field>
</record>
NB: il faut définir dans le champ « view_mode » dans Action la vue«tree »
Les interfaces
Exercice:
Compléter le module « Gestion d’école » pour permettre
l’ajout, l’affichage et la modification des enregistrements des
tables Etudiants, Classes, et Professeurs
Internationalisation
Chaque module peut fournir plusieurs traductions pour
adapter l’interface du module aux préférences de
l’utilisateur et spécialement a à la langue choisie.
Les traductions sont à créer dans un dossier nommé i18n
où chaque traduction doit être dans un fichier nommé
«LANG.po » où LANG est le code locale de la langue.
Par défaut, chaque module à un modèle de traduction
qu’on peut télécharger à partir de Odoo et qu’on peut
utiliser pour créer des traduction (internationaliser le
module)
Internationalisation
Téléchargement du modèle de traduction
En mode administrateur, cliquer le menu « Configuration
Traductions Importer / exporter Export de la
traduction»
Sélectionner dans:
langue : New language
Format du fichier: Fichier PO
Modules à exporter: le nom du module: Module de Gestion d’école
Cliquer le bouton « Exporter »
Et télécharger le fichier sur votre machine
Internationalisation
I18n
Créer un répertoire nommé «i18n » dans le dossier dumodule
Coller le fichier de localisation téléchargé précédemment«gestion_ecole.pot »
Créer une traduction en Arabe
Créer une copie de localisation fichier et renommer la copie en«ar.po ». Le code la langue peut être récupérer en cliquant le menuConfigurationTraductions Langues Code locale
Traduire les étiquettes et messages en utilisant soit un éditeur detexte simple ou un éditeur dédié de traduction (par exemple: PoEdit)
Utilisant un éditeur de texte simple, il faut ajouter la traduction de chaqueidentifiant « msgid » dans «msgstr »
msgid "Classes«
msgstr " " أقسام
Internationalisation
Appliquer la traduction
Il faut:
Redémarrer le serveur Odoo
Mettre à jour le module
Et vérifier la traduction en changeant les préférences de la langue.
Réflexion:
Créer une traduction en anglais