====== Odoo, framework de développement web ====== Odoo peut être utilisé comme un framework de développement web tout comme Django ou Flask. Odoo se différencie de ces frameworks par le fait qu'il embarque à la base des modules orientés entreprise sous forme d'applications prêtes à l'installation. Nous allons faire abstraction de ces modules pour nous concentrer sur Odoo en tant que framework de développement web en espérant que cela aidera à la prise en main de cette application par les développeurs. ===== Odoo l'application ===== Odoo est une application implémentée dans le langage python qui est un langage interprété (cet aspect a un impact sur le workflow de développement comme nous le verrons plus tard). Au démarrage, Odoo a besoin d'être préconfiguré pour se connecter au gestionnaire de base de données Postgresql. Odoo a également besoin d'être instruit des chemins (paths) où il trouvera les modules complémentaires que les développeurs ajouteront pour enrichir l'application. Ces détails sont déjà pris en charge dans l'environnement de développement qui vous est livré à votre entrée à Elosys. ===== L'environnement de développement ===== Pour favoriser un enrôlement rapide des stagiaires et des nouvelles recrues, un environnement de développement est mis à leur disposition en partie avec le dépôt https://git.elosys.net/sys/dotfiles. Cet environnement comprend des [[development:bash_shortcuts|aliases et des fonctions bash]], [[development:tmux_shortcuts|une configuration tmux]] ainsi que des dispositifs docker et docker-compose. ===== Initialiser une installation Odoo ===== Elosys vous fournira un dispositif minimal mais complet qui vous permettra de démarrer rapidement avec Odoo grâce à docker. Un dossier contenant deux fichiers ''docker-compose.yml'' et ''odoo.conf'' vous suffira pour commencer. $ cd service.odoo $ ll total 20K -rw-r--r-- 1 user user 683 juin 15 15:22 docker-compose.yml -rw-r--r-- 1 user user 1,2K juin 15 15:21 odoo.conf $ dcu La commande ''dcu'' lancera deux containers listés dans le fichier ''docker-compose.yml''. Lors de ce premier lancement, vous devez attendre que le gestionnaire de base de données Postgresql initialise son cluster ((https://www.postgresql.org/docs/current/creating-cluster.html)) . Cela devrait être indiqué par un message de type : odoo_db | PostgreSQL init process complete; ready for start up. odoo_db | odoo_db | 2022-06-15 14:23:28.769 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 odoo_db | 2022-06-15 14:23:28.769 UTC [1] LOG: listening on IPv6 address "::", port 5432 odoo_db | 2022-06-15 14:23:28.852 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" odoo_db | 2022-06-15 14:23:29.005 UTC [67] LOG: database system was shut down at 2022-06-15 14:23:28 UTC odoo_db | 2022-06-15 14:23:29.059 UTC [1] LOG: database system is ready to accept connections Une fois le cluster de base de données initialisé, arrêtez les containers en tapant ''Ctrl'' + ''c''. Le dossier qui ne contenait jusque là que deux fichiers ''docker-compose.yml'' et ''odoo.conf'' contiendra trois dossiers supplémentaires : $ ll total 20K drwx------ 19 systemd-coredump root 4,0K juin 15 15:27 data drwxr-xr-x 2 root root 4,0K juin 15 15:23 extra-addons drwxr-xr-x 2 root root 4,0K juin 15 15:23 filestore -rw-r--r-- 1 user user 683 juin 15 15:22 docker-compose.yml -rw-r--r-- 1 user user 1,2K juin 15 15:21 odoo.conf $ Vu que les dossiers spécifiques à Odoo ''extra-addons'' et ''filestore'' n'ont pas les bonnes autorisations, nous devrons lancer la commande ''ff'' pour régler le problème ((Cette commande n'est à exécuter qu'une seule fois. Il ne sera plus nécessaire de le faire plus tard)) $ ff $ ll total 20K drwx------ 19 systemd-coredump root 4,0K juin 15 15:27 data drwxr-xr-x 2 user user 4,0K juin 15 15:23 extra-addons drwxr-xr-x 2 systemd-timesync systemd-journal 4,0K juin 15 15:23 filestore -rw-r--r-- 1 user user 683 juin 15 15:22 docker-compose.yml -rw-r--r-- 1 user user 1,2K juin 15 15:21 odoo.conf $ A partir de maintenant l'environnement est prêt à recevoir vos développements. ===== Démarrer et arrêter Odoo ===== ''dcu'' et ''dcd'' sont les deux commandes utilisées pour, respectivement, démarrer et arrêter les containers Odoo et Postgresql. Nous avons auparavant vu l'usage du raccourci ''Ctrl'' + ''c'' pour arrêter les deux containers (Odoo et Postgresql) en cours d'exécution sur le même terminal. Les commandes ''dcd'' et ''dcu'' dépendent du fichier ''docker-compose.yml''. Elle doivent donc être exécutées à partir d'un dossier contenant le fichier ''docker-compose.yml'' ou dans un sous-dossier de ce dossier. Il est généralement pas nécessaire d'arrêter le container Postgresql. Ce qui nous importe le plush dans notre workflow de développement c'est de redémarrer l'application Odoo. Cette tâche peut être effectuée de manière sélective en utilisant la commande : $ docker-compose restart odoo La commande ci-dessus ne fera qu'arrêter puis démarrer l'application Odoo. Comme les commandes ''dcu'' et ''dcd'' cette commande doit être exécutée dans un dossier qui contient le fichier ''docker-compose.yml'' ou dans un sous-dossier de ce dossier. Comme nous l'avons évoqué auparavant la nature interprétée du langage python impose un certain workflow de développement que nous allons poser ici et que nous rappelleront au cours de cette introduction : Si vous modifiez un fichier python au cours de votre développement vous devez à minima((Nous verrons plus tard que cela n'est pas suffisant dans un certain cas de figure et qu'il faudra en plus upgrader son module dans le gestionnaire d'application de Odoo)) redémarrer l'application Odoo pour que vos modifications soient chargées dans le nouvel interpréteur qui portera l'application Odoo. ===== Créer votre première base de données ===== Après avoir démarré Odoo avec ''dcu'', rendez-vous avec votre navigateur sur l'adresse http://127.0.0.1:8069/. Vous serez invité à créer votre première base de données. {{ :development:odoo_create_db.png |}} Durant cette initialisation, observez le log de l'application sur votre terminal. Le log vous renseignera sur la procédure d'initialisation d'une installation Odoo de base. ((Le log de l'application est un des principaux outils de développement.)) A la fin de l'initialisation de la base de données, vous serez redirigé vers la page d'accueil de l'application Odoo. ((Dans le cas d'une installation de base sans aucun module installé, ça sera la //vue// ''Apps'' qui liste les modules disponibles à l'installation)) {{ :development:page_daccueil_odoo_base.png |}} ===== Créer un module Odoo minimal ===== Qu'est ce qu'un module Odoo ? Un module Odoo est d'abord un [[https://docs.python.org/3/tutorial/modules.html#tut-packages|paquet python]]. Un paquet python est un dossier dans lequel réside un fichier nommé ''%%__init__.py%%''. Un module Odoo est un paquet python dans lequel réside un fichier supplémentaire nommé ''%%__manifest__.py%%''. En somme, un module Odoo est un dossier dans lequel on trouve deux fichiers ''%%__init__.py%%'' et ''%%__manifest__.py%%''. Si le fichier ''%%__init__.py%%'' peut être vide, le fichier ''%%__manifest__.py%%'' doit contenir un dictionnaire qui contient au moins une valeur texte pour la clé ''name''. ((La list complète des clés valables dans un fichier manifest de Odoo est accessible [[https://www.odoo.com/documentation/15.0/developer/reference/backend/module.html|ici]])) Comme ceci : { 'name': 'Mon module', } Reste à répondre à la question : où doit-on créer ces fichiers ? Dans le dossier ''extra-addons''. Dans la suite de commandes suivante nous allons : * créer un dossier ''monmodule'' dans le dossier ''extra-addons'' * créer les fichiers ''%%__init__.py%%'' et ''%%__manifest__.py%%'' dans le dossier ''monmodule'' * remplir le fichier ''%%__manifest__.py%%'' avec le dictionnaire minimal requis. Nous allons enfin, démarrer ou redémarrer l'application Odoo. $ ll total 20K drwx------ 19 systemd-coredump root 4,0K juin 15 15:27 data drwxr-xr-x 2 user user 4,0K juin 15 15:23 extra-addons drwxr-xr-x 2 systemd-timesync systemd-journal 4,0K juin 15 15:23 filestore -rw-r--r-- 1 user user 683 juin 15 15:22 docker-compose.yml -rw-r--r-- 1 user user 1,2K juin 15 15:21 odoo.conf $ cd extra-addons $ mkdir monmodule $ cd monmodule $ touch __init__.py $ touch __manifest__.py $ echo "{'name':'Mon Module',}" | tee __manifest__.py $ docker-compose down && docker-compose up -d En se loggant sur l'application, la page d'accueil par défaut sur laquelle nous atterrissons est la vue consacrées aux applications disponibles. Nous pouvons filtrer ((Filtrer signifie taper un mot sur la barre de recherche suivi d'Enter. Nous pouvons filtrer sur le nom mais aussi sur d'autres champs comme la catégorie ou l'état. Ces notions relèvent du fonctionnel de l'application Odoo et sont abordés sur un autre support)) pour n'afficher que les modules qui correspondent au module minimal que nous venons de créer. Après avoir éliminé le filtre ''App'' nous pouvons filtrer en écrivant ''Mon module'' ou ''monmodule'' dans la barre de recherche. Ci-dessous le résultat qui devrait s'afficher. {{ :development:monmodule.png |}} ===== Récapitulatif ====== Nous avons vu dans ce qui a précédé : * Comment initialiser une installation Odoo avec docker. * Comment créer un module Odoo minimal. ===== Les modèles ===== Qu'est-ce qu'un modèle ? Un modèle est une table dans la base de données. Une des propriétés des frameworks de développement tels que Odoo est la possibilité de représenter et de manipuler la base de données sous une forme orientée objet. Ce dispositif implémenté dans Odoo s'appelle [[https://www.odoo.com/documentation/15.0/fr/developer/reference/backend/orm.html|ORM]] et nous allons l'explorer dans ce qui suit pour créer et définir des tables dans la base de données Postgresql. ===== Créer un modèle ===== Nous créons notre premier modèle dans un nouveau fichier python. $ cd ~/service.odoo/extra-addons/monmodule $ touch models.py $ editor models.py Le fichier ''models.py'' contiendra le strict minimum pour créer un model dans la base de données. from odoo import models class Professeur(models.Model): _name = 'elo.prof' Pour créer un modèle nous utilisons une classe qui hérite de la classe ''models.Model''. Le nom de la table dans la base de données (model name) est défini par la valeur de l'attribut spécial ''_name''. Pour que le nouveau fichier ''models.py'' soit chargé et évalué par l'interpréteur Python, nous devons le répertorier dans le fichier ''%%__init__.py%%'' du module, comme suit : $ cd ~/service.odoo/extra-addons/monmodule $ echo "from . import models"  | tee -a __init__.py $ cat __init__.py from . import models $ Vu que nous avons édité des fichiers python dans notre module (%%__init__.py%% et ajouté un nouveau fichier models.py), nous devons redémarrer Odoo pour que ces modifications soient chargées par l'interpréteur qui embarque l'application Odoo. Après le redémarrage d'Odoo, nous nous rendons à la vue ''App'' et nous filtrons sur notre application ''monmodule'' comme indiqué ici {{ :development:monmodule.png?linkonly |}}. Il ne reste qu'à appuyer sur le boutton ''Install''. Voici ce que nous pouvons lire dans le log de l'application après avoir appuyé sur le bouton. 01 odoo | ..... 1 INFO base odoo.addons.base.models.ir_module: ALLOW access to module.button_immediate_install on ['monmodule'] to user admin #2 via 172.17.0.1 02 odoo | ..... 1 INFO base odoo.addons.base.models.ir_module: User #2 triggered module installation 03 odoo | ..... 1 INFO base odoo.addons.base.models.ir_module: ALLOW access to module.button_install on ['monmodule'] to user admin #2 via 172.17.0.1 04 odoo | ..... 1 INFO base odoo.modules.loading: loading 1 modules... 05 odoo | ..... 1 INFO base odoo.modules.loading: 1 modules loaded in 0.03s, 0 queries (+0 extra) 06 odoo | ..... 1 INFO base odoo.modules.loading: updating modules list 07 odoo | ..... 1 INFO base odoo.addons.base.models.ir_module: ALLOW access to module.update_list on [] to user __system__ #1 via 172.17.0.1 08 odoo | ..... 1 INFO ? werkzeug: 172.17.0.1 - - [05/Jul/2022 11:18:12] "GET /web/static/src/img/spin.png HTTP/1.1" 200 - - - - 09 odoo | ..... 1 INFO base odoo.modules.loading: loading 8 modules... 10 odoo | ..... 1 INFO base odoo.modules.loading: 8 modules loaded in 0.03s, 0 queries (+0 extra) 11 odoo | ..... 1 INFO base odoo.modules.loading: loading 9 modules... 12 odoo | ..... 1 INFO base odoo.modules.loading: Loading module monmodule (2/9) 13 odoo | ..... 1 WARNING base odoo.models: The model elo.prof has no _description 14 odoo | ..... 1 INFO base odoo.modules.registry: module monmodule: creating or updating database tables 15 odoo | ..... 1 INFO base odoo.modules.loading: Module monmodule loaded in 1.43s, 35 queries 16 odoo | ..... 1 INFO base odoo.modules.loading: 9 modules loaded in 1.43s, 35 queries (+0 extra) 17 odoo | ..... 1 WARNING base odoo.modules.loading: The model elo.prof has no access rules, consider adding one. E.g. access_elo_prof,access_elo_prof,model_elo_prof,base.group_user,1,0,0,0 * À la ligne 01 l'application nous informe que la pression sur le bouton ''Install'' a bien été enregistrée et appelle la méthode correspondante ''module.button_immediate_install''. * À la ligne 12 l'application nous informe que Odoo charge notre module ''Loading module monmodule'' * À la ligne 13 l'application émet un WARNING sur l'absence de l'attribut ''_description'' pour le model ''elo.prof'' que nous nous apprêtons à créer grâce aux instructions du fichiers ''models.py'' de notre module. * À la ligne 14 l'application nous informe qu'elle est sur le point de créer les tables de base de données * Enfin, à la dernière ligne l'application émet un WARNING sur l'absence de règles d'accès sur la tables ''elo.prof'' que nous venons de créer. ((Les règles d'accès seront abordées plus loin dans ce tuto)) ===== Découvrir les modèles ===== Comment vérifier que notre modèle a bien été crée par Odoo dans la base de données ? ==== Activer le mode développeur dans Odoo ==== Même si cette page de [[https://www.odoo.com/documentation/15.0/fr/applications/general/developer_mode.html|référence du site Odoo]] présente plusieurs façons d'activer le mode développeur dans l'interface web de Odoo, nous vous recommandons d'installer l'extension **Odoo Debug** disponible pour [[https://addons.mozilla.org/fr/firefox/addon/odoo-debug/|Firefox]] et pour [[https://chrome.google.com/webstore/detail/odoo-debug/hmdmhilocobgohohpdpolmibjklfgkbi?hl=fr|Chrome]] En activant le mode développeur, vous avez accès à des menus et des fonctionnalités supplémentaires qui vous aideront à mieux introspecter votre installation Odoo. Ci-dessous, vous verrez des menus supplémentaire sur la vue ''App'' à l'activation du mode debug. {{ :development:debug_menu.png?direct |}} Une étude extensive de l'usage du debug mode est abordée sur un autre support. :todo: ==== Voir les modèles dans Odoo ==== Dans le menu ''Settings'' appuyer sur ''Technical'' et scroller jusqu'à la section ''Database Structure'' et sélectionner ''Models''. {{ :development:menu_technical_models.png?direct |}} Les modèles installés dans votre base de données sont eux-mêmes répertoriés dans un modèle spécifique du nom de ''ir.model''. Dans cette vue sur les enregistrements du model ''ir.model'', nous avons donc l'intégralité des tables existantes dans notre installation Odoo. Nous pouvons filtrer pour chercher la table que nous avons créée dans notre fichier ''models.py'' en inscrivant ''elo'' dans la zone de recherche. Notre table ''elo.prof'' devrait apparaître comme dans la capture suivante. {{ :development:model_elo.png?direct |}} En sélectionnant cet enregistrement, nous en aurons donc une vue détaillée que nous appelons **vue formulaire** (ou form view en anglais). {{ :development:model_form_view.png?direct |}} Dans cette vue nous pouvons déjà voir les champs qui ont été automatiquement créés par Odoo à la création du model. Les champs correspondent aux colonnes d'une table de base de données. Ces champs automatiques sont nécessaire au fonctionnement interne d'Odoo. Nous verrons plus tard comment ajouter nos propres champs à nos modèles. ==== Voir les modèles dans Postgresql ==== Nous allons ici apprendre comment interagir en ligne de commande avec le gestionnaire de base de données qui s'exécute à l'intérieur de docker. Pour exécuter le client psql, nous lanceront la commande suivante dans le terminal. (Il faut que le gestionnaire de base de données soit en cours d'exécution) $ docker exec -it odoo_db psql -U postgres psql (10.0) Type "help" for help. postgres=# La commande précédente nous connecte avec l'utilisateur **postgres** sur la base de données de maintenance du même nom. Nous allons dans ce qui suit * Nous connecter à la base de données nommée **base** que nous avons installée [[#creer_votre_premiere_base_de_donnees|ici]] * Affichier le détail du modèle que nous avons créer avec l'ORM d'Odoo. postgres=# \c base You are now connected to database "base" as user "postgres". base=# \d elo_prof Table "public.elo_prof" Column | Type | Collation | Nullable | Default -------------+-----------------------------+-----------+----------+-------------------------------------- id | integer | | not null | nextval('elo_prof_id_seq'::regclass) create_uid | integer | | | create_date | timestamp without time zone | | | write_uid | integer | | | write_date | timestamp without time zone | | | Indexes: "elo_prof_pkey" PRIMARY KEY, btree (id) Foreign-key constraints: "elo_prof_create_uid_fkey" FOREIGN KEY (create_uid) REFERENCES res_users(id) ON DELETE SET NULL "elo_prof_write_uid_fkey" FOREIGN KEY (write_uid) REFERENCES res_users(id) ON DELETE SET NULL base=# Relevons la différence qu'il y a entre la représentation du modèle dans Odoo et dans le gestionnaire de base de données. ''elo.prof'' dans Odoo devient ''elo_prof'' dans le gestionnaire de base de données. ===== Les champs ===== Notre modèle ne contient jusqu'à cette étape que les champs (colonnes) créés automatiquement par Odoo pour son fonctionnement interne. En général, nous aurons besoin de définir des champs supplémentaires pour porter l'identité et le fonctionnement de notre modèle. Pour cela, nous allons éditer notre modèle dans le fichier ''models.py'' pour lui ajouter de nouveaux champs. from odoo import models, fields class Professeur(models.Model): _name = 'elo.prof' _description = 'Professor Card' name = fields.Char(string="Professor Name") bio = fields.Text(string="Short Bio") Les champs des modèles sont définis comme des attributs. Ici, nous en créons deux, les champs ''name'' et ''bio'' qui sont respectivement de type ''Char'' et ''Text'' ((Pour une liste complete des types de champs référez vous à [[https://www.odoo.com/documentation/15.0/fr/developer/reference/backend/orm.html#fields|Odoo ORM]])). Les champs sont des instances de classes du module ''fields'' que nous avons importé à la première ligne en plus du module ''models''. Nous avons ajouté l'attribut spécial ''_description'' pour faire taire le **WARNING** vu lors de l'installation du module. Nous rappelons encore une fois que modifier un fichier python nécessite le **redémarrage** de l'application Odoo. Ici, vu que nous avons modifié la définition d'un model, en ajoutant ou modifiant ses champs, nous devons **en plus** upgrader le module pour que ces modifications soient répercutées dans la base de données. Pour mettre à jour notre module, nous revenons à la vue ''Apps'' et nous sélectionnons ''Upgrade'' après avoir appuyé sur les trois points d'extension verticaux dans le coin haut-droit du post-it kanban réservé à notre module. {{ :development:module_upgrade.png?direct |}} Et voici donc ce qui apparaîtera dans votre terminal après avoir appuyé sur le bouton. odoo | .... INFO base odoo.addons.base.models.ir_module: ALLOW access to module.button_immediate_upgrade on ['monmodule'] to user admin #2 via 172.17.0.1 odoo | .... INFO base odoo.addons.base.models.ir_module: ALLOW access to module.button_upgrade on ['monmodule'] to user admin #2 via 172.17.0.1 .... odoo | .... INFO base odoo.modules.loading: loading 1 modules... .... odoo | .... INFO base odoo.modules.loading: Loading module monmodule (2/9) odoo | .... INFO base odoo.modules.registry: module monmodule: creating or updating database tables .... odoo | .... WARNING base odoo.modules.loading: The model elo.prof has no access rules, consider adding one. E.g. access_elo_prof,access_elo_prof,model_elo_prof,base.group_user,1,0,0,0 odoo | .... INFO base odoo.modules.registry: verifying fields for every extended model odoo | .... INFO base odoo.modules.loading: Modules loaded. Le **WARNING** sur l'absence de description du modèle a disparu mais le WARNING sur l'absence de règles d'accès (**access rules**) est toujours présent. Nous verrons comment le traiter dans la partie suivante. Mais avant cela nous allons examiner notre modèle dans Odoo et dans Postgresql pour voir nos modifications. {{ :development:elo_prof_model_upgraded_1.png?direct |}} $ docker exec odoo_db psql -U postgres -d base -c '\d+ elo_prof' Table "public.elo_prof" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -------------+-----------------------------+-----------+----------+--------------------------------------+----------+--------------+----------------- id | integer | | not null | nextval('elo_prof_id_seq'::regclass) | plain | | create_uid | integer | | | | plain | | Created by create_date | timestamp without time zone | | | | plain | | Created on write_uid | integer | | | | plain | | Last Updated by write_date | timestamp without time zone | | | | plain | | Last Updated on name | character varying | | | | extended | | Professor Name bio | text | | | | extended | | Short Bio Indexes: "elo_prof_pkey" PRIMARY KEY, btree (id) Foreign-key constraints: "elo_prof_create_uid_fkey" FOREIGN KEY (create_uid) REFERENCES res_users(id) ON DELETE SET NULL "elo_prof_write_uid_fkey" FOREIGN KEY (write_uid) REFERENCES res_users(id) ON DELETE SET NULL $ Sur les deux représentations du modèle ''elo.prof'' nous pouvons voir que les champs ''name'' et ''bio'' ont été correctement ajoutés. ===== Les droits d'accès ===== Les droits d'accès sont le mécanisme utilisé pour définir les autorisations sur les modèles. Grâce aux droits d'accès nous pouvons dire si tel groupe d'utilisateurs a le droit de lecture, de modification, de création ou d'effacement sur les enregistrements d'un modèle. C'est un des nombreux mécanismes utilisables dans Odoo pour gérer les droits d'accès sur les modèles. Pour implémenter les droits d'accès nous devons d'abord les déclarer dans le manifeste de notre module. Nous allons donc éditer le fichier ''%%__manifest__.py%%'' ainsi : { 'name':'Mon Module', 'data': [ 'ir.model.access.csv', ], } Nous avons ajouté au manifest une nouvelle clé ''data'' dont la valeur est une liste dont chaque élément est le nom d'un fichier qui est supposé contenir de la data. Le nom de ce fichier correspond à un des modèles préchargés dans Odoo et dont Odoo se sert pour son fonctionnement interne. Le modèle ''ir.model.access'' est le modèle qu'utilise Odoo pour conserver les droits d'accès sur les autres modèles. Nous allons donc créer le ''ir.model.access.csv'' $ cd ~/service.odoo/extra-addons/monmodule $ touch ir.model.access.csv Et nous allons le remplir avec le contenu suivant : id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_elo_prof,access_elo_prof,model_elo_prof,base.group_user,1,1,1,1 |id|name|model_id:id|group_id:id|perm_read|perm_write|perm_create|perm_unlink| |access_elo_prof|access_elo_prof|model_elo_prof|base.group_user|1|1|1|1| ===== Les vues =====