Chapitre 6 : Création d’un module sur Odoo (part 1)

Odoo est disponible en deux versions : Odoo Enterprise (sous licence et sources partagées) et Odoo Community (open-source). En plus de services tels que l’assistance ou les mises à jour, la version Enterprise fournit des fonctionnalités supplémentaires à Odoo. D’un point de vue technique, ces fonctionnalités sont simplement de nouveaux modules installés en plus des modules fournis par la version Community.

Dans cette formation, on va utiliser la version gratuite nommée « Odoo community ».

1- Démarrer/arrêter le serveur Odoo

Odoo utilise une architecture client/serveur dans laquelle les clients sont des navigateurs web accédant au serveur Odoo via RPC.

La logique metier et l’extension sont généralement effectuées du côté du serveur, bien que des fonctions client (par exemple, de nouvelles représentations de données telles que des cartes interactives) puissent être ajoutées au client.

Pour démarrer le serveur, il suffit d’invoquer la commande odoo-bin dans le shell, en ajoutant le chemin complet du fichier si nécessaire :

odoo-bin

Le serveur est arrêté en appuyant deux fois sur Ctrl-C à partir du terminal, ou en tuant le processus correspondant du système d’exploitation.

Odoo fournit un mécanisme pour aider à mettre en place un nouveau module, odoo-bin a une sous-commande scaffold pour créer un module vide :

$ odoo-bin scaffold <module name> <where to put it>

La commande crée un sous-répertoire pour votre module et crée automatiquement un certain nombre de fichiers standard pour un module. La plupart d’entre eux contiennent simplement du code commenté ou du XML. L’utilisation de la plupart de ces fichiers sera expliquée tout au long de ce tutoriel.

Activité : création d’un Module vide

1- Utilisez la ligne de commande ci-dessous pour créer un module vide om_hospital dans votre dossier custom_addons, et installez-le dans Odoo.

$cd odoo16
$mkdir custom_addons
$odoo-bin scaffold om_hospital .

2- Décrire les étapes à suivre pour le faire fonctionner sur votre navigateur.

Manifest

Le fichier manifeste (__manifest__.py) est un élément essentiel d’un module Odoo. Il sert à décrire et à configurer les informations clés du module pour Odoo. Voici une présentation détaillée des sections et des champs couramment utilisés dans le fichier manifeste :

{
    'name': 'Nom du module',
    'version': '1.0',
    'summary': 'Résumé du module',
    'description': 'Description détaillée du module',
    'author': 'Auteur(s) du module',
    'website': 'Site web du module',
    'category': 'Catégorie du module',
    'depends': ['modules_dependants'],
    'data': [
        'security/security.xml',
        'views/view.xml',
        'data/data.xml',
    ],
    'demo': [
        'demo/demo_data.xml',
    ],
    'installable': True,
    'application': True,
    'auto_install': False,
}
  • 'name': Le nom du module tel qu’il apparaîtra dans l’interface Odoo.
  • 'version': La version du module.
  • 'summary': Un résumé concis du module.
  • 'description': Une description détaillée du module.
  • 'author': Le ou les auteurs du module.
  • 'website': L’URL du site web associé au module.
  • 'category': La catégorie à laquelle appartient le module (par exemple, Ventes, Comptabilité, Ressources humaines, etc.).
  • 'depends': Une liste des modules sur lesquels le module actuel dépend. Ces modules dépendants seront installés avant le module actuel.
  • 'data': Une liste des fichiers de données à inclure dans le module. Il peut s’agir de fichiers XML pour la configuration des vues, de fichiers CSV pour les données initiales, de fichiers de sécurité, etc.
  • 'demo': Une liste des fichiers de données de démonstration à inclure dans le module. Ces fichiers sont utilisés pour fournir des données de démonstration lors de l’installation du module.
  • 'installable': Un booléen indiquant si le module peut être installé ou non.
  • 'application': Un booléen indiquant si le module est une application principale.
  • 'auto_install': Un booléen indiquant si le module doit être installé automatiquement lorsque ses dépendances sont installées.

Le fichier manifeste peut également contenir d’autres champs pour spécifier des icônes, des dépendances externes, des traductions, etc., en fonction des besoins spécifiques du module.

Le fichier manifeste joue un rôle crucial lors de l’installation et de la configuration des modules Odoo. Il fournit des informations essentielles à Odoo pour gérer les dépendances, afficher les informations du module dans l’interface utilisateur et effectuer diverses opérations lors de l’installation, la mise à niveau ou la désinstallation du module.

Activité : Fichier Manifeste pour un système de gestion d’hôpital

Vous êtes chargé de développer un système de gestion d’hôpital en utilisant Odoo. Votre première tâche consiste à créer le fichier manifeste pour le module.

Votre mission est de :

  1. Créer un fichier de manifeste (__manifest__.py) avec la spécifications suivante :
    • Nom du module : « Hospital Management System »
  2. Activer le mode développeur
  3. Chercher le module « om_hospital »
  4. Mettre à jour la liste des applications
  5. Consulter les informations du module
  {
    'name': "Hospital Management System",
  }

Activité : Fichier Manifeste pour un système de gestion d’hôpital

  1. Modifier le fichier un fichier de manifeste (__manifest__.py) avec la spécifications suivante :
  • author : « Votre Nom et Prenom »
      'author' : "Abdelhak Fadili",

    Activité : Fichier Manifeste pour un système de gestion d’hôpital

    1. Modifier le fichier un fichier de manifeste (__manifest__.py) avec la spécifications suivante :
    • ‘website : « www.bestwaycoding.com »
    • ‘summary’ : « Odoo 16 Development »
          'website : "www.bestwaycoding.com",
           'summary' : "Odoo 16 Development",

      __init__.py

      Le fichier __init__.py est un fichier spécial utilisé pour indiquer à Python qu’un répertoire doit être considéré comme un package. Voici quelques utilisations courantes du fichier __init__.py :

      1. Déclaration d’un package : Lorsque vous créez un répertoire pour regrouper un ensemble de modules Python liés, vous devez inclure un fichier __init__.py vide à l’intérieur de ce répertoire. Cela indique à Python que le répertoire doit être traité comme un package, permettant l’importation des modules contenus dans ce répertoire.
      2. Initialisation de code : Le fichier __init__.py peut contenir du code qui sera exécuté lorsqu’un package est importé. Vous pouvez y placer des instructions d’initialisation, des définitions de variables, des importations supplémentaires, etc. Ce code sera exécuté automatiquement lorsque le package est importé pour la première fois.
      3. Importations groupées : Dans le fichier __init__.py, vous pouvez importer des modules spécifiques du package et les rendre disponibles pour une importation groupée. Cela facilite l’importation des modules du package en utilisant une seule instruction d’importation, plutôt que d’importer chaque module individuellement.
      4. Gestion des imports relatifs : Si vous utilisez des imports relatifs dans votre code, le fichier __init__.py peut être utilisé pour définir les chemins de recherche des imports relatifs. Cela aide à spécifier comment les modules doivent être importés en se basant sur leur position dans la structure du package.
      5. Documentation : Le fichier __init__.py peut également contenir de la documentation sous forme de docstrings. Vous pouvez fournir une description du package, des instructions d’utilisation ou toute autre information pertinente pour les utilisateurs du package.

      Voici un exemple simple d’un fichier __init__.py :

      Supposons que vous avez un package appelé « monpackage » contenant deux modules : « module1.py » et « module2.py ». Voici comment pourrait être structuré le contenu du fichier __init__.py dans le répertoire « monpackage » :

      # monpackage/__init__.py
      
      # Importation des modules du package
      from . import module1
      from . import module2
      
      # Code d'initialisation
      print("Le package monpackage a été importé avec succès.")

      Dans cet exemple, le fichier __init__.py importe les modules « module1 » et « module2 » du package « monpackage » en utilisant des imports relatifs (from . import). Cela signifie que vous pouvez ensuite importer ces modules en utilisant import monpackage.module1 et import monpackage.module2 depuis d’autres parties de votre code.

      De plus, le fichier __init__.py contient une simple instruction print pour afficher un message lors de l’importation du package. Ce code d’initialisation sera exécuté chaque fois que le package « monpackage » est importé.

      L’existence du fichier __init__.py dans le répertoire « monpackage » indique à Python que ce répertoire est un package et permet d’organiser et de structurer les modules qu’il contient.

      En résumé, le fichier __init__.py est utilisé pour indiquer qu’un répertoire est un package Python et peut contenir du code d’initialisation, des importations groupées, des définitions de variables et de la documentation. Il est important de noter que le fichier __init__.py peut être vide, mais sa présence est nécessaire pour que Python reconnaisse le répertoire comme un package.

      Activité : Création de fichier __init__.py

      1. Créer un Dossier « models ».
      2. À l’intérieur du dossier ajouter les fichiers:
        • __init__.py.
        • patient.py
      3. importer le fichier patient.py dans __init__.py
          from . import patient

      2- Object-Relational Mapping

      Un composant clé d’Odoo est la couche ORM. Cette couche évite d’avoir à écrire la plupart des SQL à la main et fournit des services d’extensibilité et de sécurité.

      Les objets métier sont déclarés comme des classes Python étendant le modèle qui les intègre dans le système de persistance automatisé.

      La couche ORM (Object Relational Mapping) d’Odoo joue un rôle essentiel dans le développement d’applications en utilisant le framework Odoo. Voici quelques façons dont la couche ORM peut vous aider :

      1. Abstraction de la base de données : La couche ORM d’Odoo fournit une abstraction de la base de données sous-jacente. Au lieu d’interagir directement avec les requêtes SQL et les tables de la base de données, vous pouvez manipuler les enregistrements et les données de manière orientée objet en utilisant les modèles et les méthodes ORM.
      2. Modélisation des données : La couche ORM permet de définir des modèles de données en tant que classes Python. Ces modèles représentent des tables de base de données et encapsulent les champs, les relations et les comportements associés aux données. Vous pouvez définir des champs de différents types (entier, texte, date, etc.), des relations (many2one, one2many, many2many) et des méthodes pour effectuer des opérations sur les données.
      3. Manipulation des données : La couche ORM facilite la manipulation des données. Vous pouvez créer, lire, mettre à jour et supprimer des enregistrements en utilisant des méthodes fournies par les modèles ORM. Par exemple, vous pouvez utiliser les méthodes create, write, unlink pour effectuer les opérations CRUD (Create, Read, Update, Delete) sur les enregistrements.
      4. Requêtes avancées : La couche ORM d’Odoo offre des fonctionnalités avancées pour effectuer des requêtes complexes sur les données. Vous pouvez utiliser des expressions de domaine pour filtrer les enregistrements, effectuer des recherches textuelles, trier les résultats, regrouper les enregistrements, effectuer des jointures de tables, etc. L’ORM facilite la construction de requêtes complexes en utilisant une syntaxe simplifiée.
      5. Sécurité des données : La couche ORM d’Odoo gère également la sécurité des données. Vous pouvez définir des règles d’accès aux enregistrements basées sur des groupes d’utilisateurs et des règles de sécurité. L’ORM vérifie automatiquement les autorisations d’accès lors des opérations sur les enregistrements, garantissant ainsi que seuls les utilisateurs autorisés peuvent effectuer des actions spécifiques.
      6. Portabilité : L’utilisation de la couche ORM d’Odoo facilite la portabilité de votre application. Étant donné que la couche ORM abstrait les détails spécifiques à la base de données, votre code fonctionnera sur différents types de bases de données pris en charge par Odoo (tels que PostgreSQL, MySQL, SQLite).

      Définir les modèles

      Dans Odoo, les modèles, également connus sous le nom de classes de modèle, sont des composants essentiels pour la définition de la structure des données et de la logique métier d’une application. Les modèles sont définis dans les modules Odoo et représentent des entités telles que des objets, des tables de base de données ou des documents.

      La classe de modèle est déclarée en utilisant le décorateur @api.model ou en héritant de la classe de base odoo.models.Model. Les modèles définissent les champs, les méthodes, les relations et les comportements spécifiques de l’entité qu’ils représentent. Voici quelques éléments importants que vous pouvez trouver dans la définition d’un modèle :

      1. Champs : Les champs sont les attributs qui définissent les propriétés et les données d’un modèle. Les types de champs couramment utilisés incluent Char (chaîne de caractères), Integer (entier), Float (nombre décimal), Boolean (booléen), Date (date), Datetime (date et heure), Selection (sélection de choix), etc.
      2. Méthodes : Les méthodes sont des fonctions définies à l’intérieur de la classe de modèle. Elles sont utilisées pour implémenter la logique métier spécifique au modèle. Par exemple, vous pouvez définir des méthodes pour effectuer des calculs, des validations, des actions ou des opérations spécifiques liées aux données du modèle.
      3. Relations : Les relations sont des liens entre différents modèles. Elles permettent d’établir des liens logiques et de récupérer des informations d’autres modèles. Les types de relations couramment utilisés incluent Many2one (relation vers un modèle parent), One2many (relation vers plusieurs modèles enfants) et Many2many (relation de nombreux-à-plusieurs).
      4. Contraintes : Les contraintes sont des règles qui sont appliquées lors de la création ou de la modification des enregistrements du modèle. Elles sont utilisées pour valider les données et garantir l’intégrité des données. Par exemple, vous pouvez définir une contrainte pour s’assurer qu’un champ obligatoire est renseigné ou qu’une valeur respecte certaines conditions.

      Les modèles Odoo offrent une flexibilité et une puissance considérables pour définir la structure des données et la logique métier d’une application. Ils permettent de manipuler, de rechercher, de créer et de modifier des enregistrements dans la base de données, ainsi que d’interagir avec d’autres modèles et de mettre en œuvre des fonctionnalités spécifiques.

      Il est important de bien comprendre la structure et les fonctionnalités des modèles dans Odoo pour développer efficacement des applications personnalisées ou étendre les fonctionnalités existantes d’Odoo. La documentation officielle d’Odoo fournit des informations détaillées sur la création et l’utilisation des modèles.

      Les modèles peuvent être configurés en définissant un certain nombre d’attributs lors de leur définition. L’attribut le plus important est _name qui est obligatoire et définit le nom du modèle dans le système Odoo. Voici une définition minimale complète d’un modèle :

      from odoo import models
      class MinimalModel(models.Model):
          _name = 'test.model'

      Voici un exemple qui illustre l’utilisation de différents types de champs dans Odoo :

      from odoo import models, fields
      
      class ExampleModel(models.Model):
          _name = 'example.model'
      
          name = fields.Char(string='Name', required=True)
          age = fields.Integer(string='Age')
          salary = fields.Float(string='Salary')
          is_employee = fields.Boolean(string='Is Employee')
          birthdate = fields.Date(string='Birthdate')
          created_at = fields.Datetime(string='Created At')
          status = fields.Selection([
              ('draft', 'Draft'),
              ('in_progress', 'In Progress'),
              ('done', 'Done')
          ], string='Status', default='draft')
          category_ids = fields.Many2many('example.category', string='Categories')
          parent_id = fields.Many2one('example.model', string='Parent')
          child_ids = fields.One2many('example.model', 'parent_id', string='Children')
      
      class ExampleCategory(models.Model):
          _name = 'example.category'
      
          name = fields.Char(string='Name')

      Dans cet exemple, nous avons défini un modèle ExampleModel avec différents types de champs :

      • Char : Le champ name est une chaîne de caractères (texte) et est requis (required=True).
      • Integer : Le champ age est un entier.
      • Float : Le champ salary est un nombre décimal.
      • Boolean : Le champ is_employee est un booléen, qui indique si la personne est employée ou non.
      • Date : Le champ birthdate est une date.
      • Datetime : Le champ created_at est une date et heure.
      • Selection : Le champ status est une sélection de choix avec les valeurs ‘draft’, ‘in_progress’ et ‘done’.
      • Many2many : Le champ category_ids établit une relation de nombreux-à-plusieurs avec le modèle example.category, permettant de lier plusieurs catégories à un enregistrement.
      • Many2one : Le champ parent_id établit une relation vers un enregistrement parent du même modèle.
      • One2many : Le champ child_ids établit une relation vers plusieurs enregistrements enfants du même modèle.

      Notez que dans cet exemple, nous avons également défini un modèle ExampleCategory pour illustrer la relation Many2many avec le modèle ExampleModel.

      Cet exemple couvre différentes possibilités de types de champs dans Odoo. Vous pouvez utiliser ces types de champs en fonction de vos besoins métier spécifiques et de la structure des données de votre application.

      Les relations Many2many, One2many et Many2one sont des types de relations utilisés pour établir des liens entre différents modèles dans Odoo. Voici une explication détaillée de chaque type de relation :

      1. Relation Many2many :
      • Utilisation : Cette relation est utilisée lorsqu’il y a une relation de nombreux-à-plusieurs entre deux modèles. Par exemple, une personne peut appartenir à plusieurs groupes et un groupe peut contenir plusieurs personnes.
      • Déclaration : Pour déclarer une relation Many2many, vous devez utiliser le champ Many2many et spécifier le modèle cible avec lequel vous voulez établir la relation.
      • Exemple : category_ids = fields.Many2many('example.category', string='Categories')
      • Dans cet exemple, le champ category_ids du modèle ExampleModel établit une relation Many2many avec le modèle example.category, permettant à un enregistrement de ExampleModel d’appartenir à plusieurs catégories.
      1. Relation One2many :
      • Utilisation : Cette relation est utilisée lorsqu’il y a une relation de un-à-plusieurs entre deux modèles. Par exemple, un projet peut avoir plusieurs tâches liées.
      • Déclaration : Pour déclarer une relation One2many, vous devez utiliser le champ One2many et spécifier le modèle cible avec lequel vous voulez établir la relation. Vous devez également spécifier le champ inverse sur le modèle cible pour lier les enregistrements.
      • Exemple : task_ids = fields.One2many('project.task', 'project_id', string='Tasks')
      • Dans cet exemple, le champ task_ids du modèle Project établit une relation One2many avec le modèle project.task, en utilisant le champ project_id sur le modèle cible pour lier les enregistrements. Cela permet à un projet d’avoir plusieurs tâches liées.
      1. Relation Many2one :
      • Utilisation : Cette relation est utilisée lorsqu’il y a une relation de plusieurs-à-un entre deux modèles. Par exemple, une tâche peut être liée à un projet spécifique.
      • Déclaration : Pour déclarer une relation Many2one, vous devez utiliser le champ Many2one et spécifier le modèle cible avec lequel vous voulez établir la relation.
      • Exemple : project_id = fields.Many2one('project.project', string='Project')
      • Dans cet exemple, le champ project_id du modèle Task établit une relation Many2one avec le modèle project.project, permettant de lier une tâche à un projet spécifique.

      Ces types de relations sont essentiels pour modéliser les relations complexes entre les différentes entités d’une application Odoo. En utilisant ces relations, vous pouvez établir des liens logiques et récupérer des informations d’autres modèles, ce qui facilite la manipulation et l’organisation des données dans Odoo.

      Champs du modèle

      Les champs sont utilisés pour définir ce que le modèle peut stocker et où. Les champs sont définis comme des attributs de la classe de modèle :

      from odoo import models, fields
      
      class LessMinimalModel(models.Model):
          _name = 'test.model2'
      
          name = fields.Char()
      

      Attributs communs

      Tout comme le modèle lui-même, ses champs peuvent être configurés en passant des attributs de configuration en tant que paramètres :

      name = fields.Char(required=True)
      

      Dans Odoo, il existe certains attributs communs qui peuvent être utilisés lors de la définition des champs d’un modèle. Ces attributs fournissent des fonctionnalités supplémentaires et permettent de personnaliser le comportement des champs. Voici quelques-uns des attributs communs les plus couramment utilisés :

      1. string : Cet attribut permet de définir l’étiquette ou le libellé associé à un champ. Il est utilisé pour afficher un texte descriptif dans l’interface utilisateur d’Odoo. Par exemple : name = fields.Char(string='Name').
      2. required : Cet attribut indique si le champ est obligatoire lors de la création ou de la modification d’un enregistrement. Lorsque required=True, le champ doit être renseigné. Par défaut, required est défini sur False. Par exemple : name = fields.Char(string='Name', required=True).
      3. readonly : Cet attribut spécifie si le champ est en lecture seule, ce qui signifie qu’il ne peut pas être modifié. Lorsque readonly=True, le champ est affiché en tant que valeur non modifiable dans l’interface utilisateur. Par défaut, readonly est défini sur False. Par exemple : name = fields.Char(string='Name', readonly=True).
      4. default : Cet attribut permet de définir une valeur par défaut pour un champ lors de la création d’un nouvel enregistrement. La valeur par défaut peut être une valeur statique ou une fonction qui renvoie une valeur. Par exemple : name = fields.Char(string='Name', default='Default Name').
      5. compute : Cet attribut permet de définir une fonction de calcul pour le champ. La fonction de calcul est exécutée chaque fois que la valeur du champ est accédée. Elle est utilisée pour calculer dynamiquement la valeur du champ en fonction d’autres champs ou de la logique métier. Par exemple :
      def _compute_total_amount(self):
          for record in self:
              record.total_amount = record.price * record.quantity
      
      total_amount = fields.Float(string='Total Amount', compute='_compute_total_amount')
      1. inverse : Cet attribut permet de définir une fonction inverse pour le champ. La fonction inverse est appelée lorsque la valeur du champ est modifiée et permet de mettre à jour d’autres champs ou de réaliser des actions spécifiques en fonction de cette modification. Par exemple :
      def _inverse_total_amount(self):
          for record in self:
              record.price = record.total_amount / record.quantity
      
      total_amount = fields.Float(string='Total Amount', compute='_compute_total_amount', inverse='_inverse_total_amount')
      1. store : Cet attribut indique si la valeur du champ doit être stockée dans la base de données. Par défaut, store est défini sur True. Cependant, pour les champs calculés (compute), store doit être défini sur False, car ils sont automatiquement calculés à partir d’autres champs et n’ont pas besoin d’être stockés.

      Ces attributs communs permettent de personnaliser le comportement des champs et d’ajouter des fonctionnalités supplémentaires à votre modèle dans Odoo. Vous pouvez utiliser ces attributs en fonction des besoins spécifiques de votre application pour rendre vos champs plus interactifs, dynamiques et personnalisés.

      Activité : Création d’un modèle de gestion des patients d’un hôpital

      1. Créez un modèle appelé HospitalPatient pour gérer les informations des patients dans un hôpital.
      from odoo import api,models, fields
      
      class HospitalPatient(models.Model):
          _name = 'hospital.patient'

        Activité : Création d’un modèle de gestion des patients d’un hôpital

        • Ajouter le champs ‘name’ au model « patient »
           name = fields.Char(string='Name',required=True)
        • Mettre à jour l’application
        • Verifier l’ajout de du champs dans la base de données

        Activité : Création d’un modèle de gestion des patients d’un hôpital

        • Ajouter la description du model « patient »
           _description = "Patient Records"
        • Mettre à jour l’apllication
        • Verifier la modification dans la base de données

        Activité : Création d’un modèle de gestion des patients d’un hôpital

        • Ajouter les champs ‘age’,’is child’ et ‘gender’ au model « patient »
           age = fields.Integer(string='Age')
           is_child = fields.Boolean(string='Is Child ?')
           notes = fileds.Text(string='Notes')
           gender = fields.Selection([('male','Male'),('female','Female')],string='Gender')
        • Mettre à jour l’apllication
        • Vérifier l’ajout des champs dans la base de données