Définition des contraintes et des champs calculés sur les modèles

Introduction

champs calculés odoo

Pour définir les règles métiers d’un domaine, nous aurons besoin d’ajouter des règles particulières sur des champs, et de faire des calculs sur certains d’autres.
Pour définir les règles sur des champs, Odoo utilise les contraintes de validation dans but d’éviter l’introduction erronée des données.
Les calculs se font à travers les champs calculés, qui sont des champs normaux avec la particularité qu’ils exécutent des fonctions de calcul.
Dans cet article, nous allons découvrir comment définir les règles d’introduction de certaines données, en utilisant les contraintes SQL et Python, et comment définir les champs calculés pour répondre à un besoin spécifique. 

Les types de Contraintes

Dans Odoo, il est possible de définir deux types de contraintes :
·         Contraintes au niveau Base de données SQL
·         Contraintes au niveau Serveur avec le langage python

Ajouter les contraintes SQL :  

Dans les cas simple, nous pouvons utiliser les contraintes SQL, à travers l’attribut _sql_constraint au début des classes, puis utiliser les trois mots de contrôle CHECK , UNIQUE , EXCLUDE  , pour former des expressions de contrôles.
Dans notre exemple, agence.vehicule, nous devons avoir pour chaque véhicule, un matricule unique, et on doit s’assurer que le nombre de places soit supérieur ou égale à 2.
 Pour vérifier ces deux conditions nous utilisons la contrainte SQL ‘UNIQUE’ et ‘CHECK’ .
Pour se faire, nous ajoutons le code suivant dans la classe AgenceVehicule , au niveau des attributs :
# Ajout des contraites SQL
    _sql_constraints =[
        ('mat_uniq', 'UNIQUE (matricule)',
         'Les Matricules doivent être uniques !'
        ),
        ('nb_place_positif', 'CHECK (nb_places >=2)',
         'Le nombre de place doit être suppérieur ou égale à 2 !'
         )
    ]
Comme vous pouvez le constater, on peut définir plusieurs règles sur la même contrainte, avec l’utilisation d’une liste dont chaque élément contient :
·  Le nom de la contrainte
·  La condition
·  Le message d’erreur à afficher, en cas de non vérification de la condition. 

Ajouter des contraintes Python

 Dans des cas plus complexes, il est conseillé d’utiliser les contraintes python.  Par exemple, dans notre modèle agence.vehicule, si on souhaite éviter la saisie des dates dans lefuture, nous ajoutons  une  méthode python précédée du décorateur @api.constrains(date_achat), qui permet de vérifier la validité de la date d’achat saisie.
from odoo import models, api, fields
from odoo.exceptions import ValidationError
# Ajouter des contraintes Python
    @api.constrains('date_achat')
    def _check_date_achat(self):
        for record in self:
            if record.date_achat and record.date_achat > fields.Date.today():
                raise models.ValidationError('La date d''achat doit être dans le passé ! ')

Les Champs calculés :

Parfois, nous aurons besoins de faire des calculs durant la manipulation des enregistrements en cours. Dans ces cas, on utilise des champs calculés, qui sont des champs normaux avec la particularité qu’ils font appel à des fonctions qui fassent des calculs automatiques, en se basant sur des paramètres.
La mise à jour des champs calculé se fait sur place à chaque modification des paramètres.
Si on prend notre exemple, pour connaître l’âge d’un véhicule au sein de l’entreprise, on calcule la différence entre la date du jour et la date d’achat du véhicule, ce calcul est possible à travers l’ajout d’une fonction qui a comme paramètre la date d’achat, et qui a comme valeur envoyée l’âge en années du véhicule en cours. 
Pour se faire, on doit déclarer un nouveau champ (nommez le age_veh par exemple), qui a pour rôle de calculer et de sauvegarder l’âge du véhicule en cours à partir de la date d’achat.
Editez donc le fichier agence.vehicule et ajoutez le code suivant :
from datetime import timedelta
    age_veh = fields.Float(
        string='Age du Véhicule en Années',
        compute='_compute_age',
        digits=(14,0),
        store=True,   
        compute_sudo=True
    )
    @api.depends('date_achat')
    def _compute_age(self):
        today = fields.Date.today()
        for vehicule in self:
            if vehicule.date_achat:
                delta = today - vehicule.date_achat
                vehicule.age_veh = delta.days / 365
            else:
                vehicule.age_veh = 0

   
Remarquez l’introduction des deux paramètres optionnels : 
 store=True,  ce paramètre a pour objectif d’enregistrer l’enregistrement calculé, dans ce cas il sera considéré comme les champs normaux, on peut le rechercher ou faire le tri selon ce champ. Dans le cas contraire le résultat de calcul ne sera pas enregistré et on doit définir le paramètre _serarch pour pouvoir le rechercher.  
compute_sudo=True , le rôle de ce paramètre est de pouvoir effectuer les calcul sur le champ même dans le cas où l’utilisateur en cours n’a pas les droits nécessaires.
 
Maintenant, si on veut par exemple, savoir la valeur du véhicule après x(années) qui, et que la valeur diminue chaque année d’un dixième de sa valeur d’achat initial, nous pouvons ajouter deux autres champs calculés.
Le premier champ qu’on le nomme val_amortissement :  calcule l’amortissement annuel et dépends de la valeur d’achat du véhicule
Le deuxième champ qu’on le nomme val_apres_amortissement: calcule la valeur du véhicule après l’amortissement et qui dépond de deux champs, l’âge et la valeur de l’amortissement annuel.
On peut intégrer le calcul de l’amortissement annuel directement dans le deuxième champ, mais nous avons décomposé le calcul pour démontrer les possibilités des champs calculés.    
Le code correspondant à ces champs sera comme suit :
# Ajout des champs calculés
    age_veh = fields.Float(
        string='Age du Véhicule en Années',
        compute='_compute_age',
        digits=(14,0),
        #inverse='_inverse_age', Optionnel non activé
        #search='_search_age', Optionnel non activé
        store=True,  # optional activé
        compute_sudo=True  # optional activé
    )
    @api.depends('date_achat')
    def _compute_age(self):
        today = fields.Date.today()
        for vehicule in self:
            if vehicule.date_achat:
                delta = today - vehicule.date_achat
                vehicule.age_veh = delta.days / 365
            else:
                vehicule.age_veh = 0
    # 
    val_amortissement= fields.Float(
        string='Valeur Amortissement chaque année',
        digits=(14, 2),
        compute='_compute_val_amorti',
        store=True,
    )
    @api.depends('prix_achat_v')
    def _compute_val_amorti(self):
        for vehicule in self:
            if vehicule.prix_achat_v:
                vehicule.val_amortissement = vehicule.prix_achat_v/ 10
            else:
                vehicule.val_amortissement= 0
    val_apres_amortissement = fields.Float(
        string='Valeur Après Amortissement',
        digits=(14, 2),
        compute='_compute_val_apres_amorti',
        store=True,
    )
    @api.depends('age_veh','val_amortissement')
    def _compute_val_apres_amorti(self):
        for vehicule in self:
            if vehicule.val_amortissement:
        vehicule.val_apres_amortissement=vehicule.prix_achat_v-((vehicule.age_veh)*vehicule.val_amortissement )
            else:
                vehicule.val_apres_amortissement = 0

Les fonctions relatives aux champs calculés, ont besoin d’une ou plusieurs paramètres, et doit être précédés par le décorateur @api.depends suivi par les paramètres qui entrent dans le calcul de la fonctions, ces paramètres sont insérés  entre deux parenthèses et séparés par des virgules, comme l’exemple :     @api.depends('age_veh','val_amortissement')

Affichage des nouveaux champs calculés dans la vue formulaire :

Afin de pouvoir tester le résultat de l’ajout de ces champs calculés, on doit les ajouter dans la vue agence.vehicule.xml, dans l’ordre souhaité, mais de préférence mettre les champs :
·         Date d’achat, la valeur d’achat, L’age du véhicule, la valeur d’amortissement et la valeur de véhicule après amortissement, l’un après l’autres ou à côté, pour pouvoir afficher les changements intensément dans le même emplacement.  
Pour se faire, éditez le fichier agence.vehicule.xml, particulièrement la vue formulaire(id="agence_vehicule_view_form")  de la manière suivante (modifiez l’ordre d’affichage des champs prix_achat_v et date_achat, puis ajoutez les champs age_veh, val_amortissement et  
val_apres_amortissement.
                       <field name="prix_achat_v"/>
                        <field name="date_achat"/>
                        <field name="age_veh"/>
                        <field name="val_amortissement"/>
                        <field name="val_apres_amortissement"/>
Vous pouvez à présent redémarrer le serveur et mettre à jour l’application, pour consulter les modifications apportées.

Afficher les champs à partir d’autres modèles :

related fields in Odoo

Parfois, nous avons besoin d’afficher des champs qui n’appartiennent pas aux champs du modèle en cours.  Par exemple, si on veut afficher le logo du véhicule à partir du modèle véhicule en  cours, on doit utiliser l’attribut related, qui va récupérer la valeur depuis le modèle agence.marque.
Pour se faire ajouter le champ logo_v en mettant le code suivant :
   logo_v = fields.Binary(
        'Logo de Véhicule',
        related='marque_id.logo',
        store=True,
        readonly=True)

Vous remarquez que ce champ est finalement un champ calculé, qui va afficher récupérer la valeur d’un champ d’un autre modèle(agence.marque) en utilisant son l’identifiant(marque_id) suivi par un point puis le nom du champ à récupérer , et l’affecter sur le champ du modèle en cours (agence.vehicule).
Comme dans le cas des champs calculés, les paramètres store et autres paramètres peuvent être utilisés ici.
Pour comprendre l’utilité de la fonction du nouveau champ, ajoutez le champ logo_v, dans la vue formulaire du fichier ‘agence.vehicule.xml’, de la manière suivante :
 <field name="logo_v" widget="image" class="oe_avatar"/>
Après redémarrage du service Odoo et mise à jour de l’application Location de véhicule, vous pouvez tester l’utilisation de cette nouvelle fonctionnalité très pratique.  

Conclusion

A travers cet article, nous avons étudié les possibilités qu’Odoo offrent pour pouvoir représenter la logique métier en contrôlant les données saisies et en faisant des calculs nécessaires à nos besoins.
Dans un cas pratique, nous avons utilisé les contraintes SQL et Python pour éviter l’entrée erronée des données, et le concept des champs calculés afin d’effectuer les calculs sur les champs de l’objet en cours.

Télécharger gratuitement votre  guide odoo ! 

Télécharger Gratuitement votre guide Odoo

Découvrez comment , Odoo  est une vraie opportunité pour les entreprises et les développeurs !


Télécharger !