Étape 3 sur 3 · 30 min

Afficher les formations dans le back-office

Vous allez donner des droits aux utilisateurs internes, construire une list view et une form view, puis vérifier que chaque formation affiche les notes qui lui sont associées.

1. Donnez accès aux utilisateurs internes

Créer une view ne donne aucun droit sur un model. Pour cet exercice, les membres du groupe base.group_user pourront gérer les formations. Créez le CSV suivant.

custom_addons/elearning_portal/security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_elearning_module_user,elearning.module user,model_elearning_module,base.group_user,1,1,1,1

model_elearning_module est l’external ID de la fiche technique qu’Odoo crée pour le model elearning.module. base.group_user correspond aux utilisateurs internes : cette ACL ne rend pas les formations publiques sur Internet.

Résultat attendu après la mise à niveau : un utilisateur interne pourra lire, créer, modifier et supprimer des formations. Les notes gardent leurs propres ACL et leurs éventuelles record rules dans training_hello.

2. Ajoutez la liste, la fiche et le menu

La list view donne une vue d’ensemble. Dans la form view, une liste intégrée affiche le titre et la catégorie de chaque note associée. Ces records viennent toujours de training.note.

custom_addons/elearning_portal/views/elearning_module_views.xml
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
    <record id="elearning_module_view_list" model="ir.ui.view">
        <field name="name">elearning.module.list</field>
        <field name="model">elearning.module</field>
        <field name="arch" type="xml">
            <list>
                <field name="name"/>
                <field name="note_ids"/>
            </list>
        </field>
    </record>

    <record id="elearning_module_view_form" model="ir.ui.view">
        <field name="name">elearning.module.form</field>
        <field name="model">elearning.module</field>
        <field name="arch" type="xml">
            <form>
                <sheet>
                    <group>
                        <field name="name"/>
                        <field name="description"/>
                    </group>
                    <notebook>
                        <page string="Notes associées">
                            <field name="note_ids">
                                <list>
                                    <field name="title"/>
                                    <field name="category_id"/>
                                </list>
                            </field>
                        </page>
                    </notebook>
                </sheet>
            </form>
        </field>
    </record>

    <record id="elearning_module_action" model="ir.actions.act_window">
        <field name="name">Formations</field>
        <field name="res_model">elearning.module</field>
        <field name="view_mode">list,form</field>
    </record>

    <menuitem id="elearning_portal_menu_root"
              name="Portail de formations" sequence="40"/>
<menuitem id="elearning_module_menu"
              name="Formations" parent="elearning_portal_menu_root"
              action="elearning_module_action" sequence="10"/>
</odoo>

Les fields simples restent dans le <group>. La liste de notes, elle, est placée dans une page du <notebook> : elle dispose ainsi de la largeur de la fiche. Cet onglet est un choix de présentation clair pour une relation qui peut contenir plusieurs lignes.

Comment Odoo trouve les views

Le nom elearning_module_view_list est un external ID. Vous le choisissez librement, à condition qu’il soit unique dans l’addon. Le suffixe _list est seulement une convention utile pour reconnaître la view ; il n’est relié à rien automatiquement.

La ligne <field name="arch" type="xml"> contient l’architecture de l’écran : autrement dit, le XML qui décrit ce qu’Odoo doit afficher. La balise qui ouvre cette architecture indique le type de view : <list> pour une liste, <form> pour une fiche. Dans l’action, view_mode="list,form" demande ces deux types de view ; il ne cherche pas des IDs qui finissent par _list ou _form.

Dans cet exemple, l’action ne désigne aucune view par son external ID. Odoo cherche donc une view principale du model elearning.module pour chaque type demandé. S’il en trouve plusieurs, il prend d’abord celle dont la priority est la plus basse. À priorité égale, son choix dépend encore du nom puis de l’identifiant technique : ce n’est pas une situation sur laquelle il faut compter.

Si vous avez vraiment deux views du même type

Ne créez pas deux views principales concurrentes par accident. Soit vous donnez une priority explicite à la view à utiliser en premier, soit vous liez une view précise à l’action. Lorsque vous voulez seulement modifier une view existante, créez plutôt une view héritée avec inherit_id : Odoo combine alors les deux architectures au lieu d’en choisir une au hasard.

En Odoo 19, utilisez la balise <list> ; l’ancienne balise <tree> ne convient plus. L’action porte l’external ID complet elearning_portal.elearning_module_action. Vous le réutiliserez plus tard pour créer un lien vers la fiche interne.

À observer : note_ids apparaît dans la liste générale. Dans une fiche, l’onglet affiche les fields title et category_id des notes liées. Vous les utilisez sans les redéfinir.

3. Terminez le manifeste

Remplacez la liste data vide par les trois chemins. La dépendance reste limitée à training_hello tant qu’aucune page Website n’existe.

custom_addons/elearning_portal/__manifest__.py · version complète à ce stade
{
    "name": "Portail de formations",
    "summary": "Relier des formations aux notes du module de formation",
    "version": "19.0.1.0.0",
    "category": "Training",
    "author": "Formation Odoo",
    "license": "LGPL-3",
    "depends": ["training_hello"],
    "data": [
        "security/ir.model.access.csv",
        "data/elearning_module_data.xml",
        "views/elearning_module_views.xml",
    ],
    "application": True,
    "installable": True,
}

À vérifier : les trois chemins sont relatifs à custom_addons/elearning_portal/. Odoo charge l’ACL, puis les records de formation et enfin les écrans qui les affichent.

4. Installez l’addon et vérifiez le résultat

  1. Redémarrez Odoo afin qu’il relise les imports Python et le manifeste.
  2. Activez le mode développeur, puis choisissez Apps → Mettre à jour la liste des applications.
  3. Recherchez Portail de formations et cliquez sur Installer. Si l’addon était déjà installé pendant un essai, choisissez Mettre à niveau.
  4. Ouvrez Portail de formations → Formations, puis la fiche Bases du développement Odoo.

Résultat attendu : la liste contient deux formations. La première affiche les notes Bienvenue et Observer avant de modifier ; la seconde affiche Une base d’exercice. En cliquant sur une note, vous ouvrez le record géré par training_hello.

Ce qui est commun, ce qui reste séparé

Les deux addons utilisent la même base, le même ORM et les mêmes mécanismes de droits Odoo. En revanche, elearning_portal reste responsable des formations, tandis que training_hello reste responsable des notes et de leur contenu.

5. Si cela ne fonctionne pas, vérifiez dans cet ordre

  • custom_addons/elearning_portal/__init__.py — contrôlez l’import — la valeur attendue est from . import models.
  • custom_addons/elearning_portal/models/__init__.py — contrôlez l’import — la valeur attendue est from . import elearning_module.
  • custom_addons/elearning_portal/__manifest__.py — contrôlez depends — la valeur attendue contient training_hello.
  • custom_addons/elearning_portal/data/elearning_module_data.xml — contrôlez chaque ref() — la valeur attendue commence par training_hello..
  • custom_addons/elearning_portal/security/ir.model.access.csv — contrôlez group_id:id — la valeur attendue est base.group_user.
  • custom_addons/elearning_portal/views/elearning_module_views.xml — contrôlez la racine des listes — la balise attendue est <list>.

Après une correction Python, redémarrez Odoo. Après une correction du manifeste, d’un CSV, d’un XML ou des données, mettez l’addon à niveau. Recharger uniquement le navigateur ne suffit pas : aucun de ces fichiers ne serait relu dans la base.

Résultat attendu de la section

Vous avez créé un addon distinct, un model de formations, une relation Many2many vers les notes, des données qui relient les deux addons, une ACL interne et des écrans pour observer cette relation.