Se rendre au contenu

Vues Kanban, Graph et Pivot en Odoo 19 : QWeb, widgets et dashboards

Bloc 4 · Interface utilisateur — Article 2/4 Vues Kanban, Graph et Pivot en Odoo 19 Transformer ton module en tableau de bord avec une vue Kanban…
26 avril 2026 par
Vues Kanban, Graph et Pivot en Odoo 19 : QWeb, widgets et dashboards
B.Mustapha

Bloc 4 · Interface utilisateur — Article 2/4

Vues Kanban, Graph et Pivot en Odoo 19

Transformer ton module en tableau de bord avec une vue Kanban drag-and-drop, des progressbar, des widgets priority / state_selection, puis compléter par Graph et Pivot.

~16 minutes de lecture

Vue kanban des tickets helpdesk avec 3 colonnes Nouveau En cours Résolu et progressbars
Objectif de l'article — la vue Kanban finale : colonnes Nouveau / En cours / Résolu, progressbars colorées (rouge = bloqué, vert = prêt), étoiles de priorité, badges de deadline, tags et avatars utilisateur.

Ce que tu vas apprendre

Kanban QWeb

Les templates <t t-name="card"> pour composer tes cartes.

Drag-and-drop

default_group_by — faire glisser une carte change le champ groupé.

Widgets visuels

priority, state_selection, progressbar, many2one_avatar_user, many2many_tags.

Graph & Pivot

Deux vues analytiques quasi-gratuites pour dashboards.

Prérequis

  • Module odooskills_helpdesk v19.0.1.8.0 (T16).
  • Vues Form / List / Search déjà en place + menus fonctionnels.
  • Notions de QWeb : t-if, t-attf-class, t-out.

1. Deux champs à ajouter au modèle

Pour profiter pleinement du Kanban, on ajoute deux Selection conventionnels : priority (1 à 3 étoiles) et kanban_state (bouton d'état vert/gris/rouge).

# models/helpdesk_ticket.py

priority = fields.Selection(
    selection_add=[
        ('0', 'Normale'),
        ('1', 'Importante'),
        ('2', 'Haute'),
        ('3', 'Urgente'),
    ],
    ondelete={'1': 'set default', '2': 'set default', '3': 'set default'},
    string='Priorité',
    default='0',
    tracking=True,
)

kanban_state = fields.Selection(
    selection=[
        ('normal', 'En cours'),
        ('done', 'Prêt'),
        ('blocked', 'Bloqué'),
    ],
    string='État kanban',
    default='normal',
    tracking=True,
)
Le piège priority — ce champ est déjà défini par héritage (dans notre cas via mail.thread). Utiliser selection= génère un warning "overrides existing selection; use selection_add instead". On emploie donc selection_add et on précise ondelete pour chaque valeur ajoutée — règles appliquées si une valeur est supprimée (désinstallation du module).

2. La vue Kanban complète

Le Kanban est essentiellement un template QWeb appliqué à chaque enregistrement. Voici la vue complète, commentée :

<record id="view_helpdesk_ticket_kanban" model="ir.ui.view">
    <field name="name">helpdesk.ticket.kanban</field>
    <field name="model">helpdesk.ticket</field>
    <field name="arch" type="xml">
        <kanban default_group_by="state" group_edit="false">
            <!-- Champs nécessaires au rendu mais non explicitement utilisés -->
            <field name="is_overdue"/>
            <field name="kanban_state"/>

            <!-- Barre de progression en haut de chaque colonne -->
            <progressbar field="kanban_state"
                         colors='{"done": "success", "normal": "muted", "blocked": "danger"}'/>

            <templates>
                <t t-name="card" class="oe_kanban_card">
                    <div class="oe_kanban_details">
                        <div class="d-flex justify-content-between">
                            <strong class="o_kanban_record_title">
                                <field name="reference"/>
                            </strong>
                            <field name="priority" widget="priority"/>
                        </div>
                        <div class="mt-1"><field name="name"/></div>
                        <div class="text-muted small mt-1">
                            <field name="partner_id"/>
                            <span t-if="record.category_id.raw_value"> · </span>
                            <field name="category_id"/>
                        </div>
                        <div class="mt-2">
                            <field name="tag_ids" widget="many2many_tags"
                                   options="{'color_field': 'color'}"/>
                        </div>
                        <div class="d-flex justify-content-between align-items-center mt-2">
                            <span t-if="record.deadline.raw_value"
                                  t-attf-class="badge #{record.is_overdue.raw_value ? 'text-bg-danger' : 'text-bg-light'}">
                                <i class="fa fa-calendar" title="Échéance"/>
                                <field name="deadline"/>
                            </span>
                            <div>
                                <field name="kanban_state" widget="state_selection"/>
                                <field name="user_id" widget="many2one_avatar_user"/>
                            </div>
                        </div>
                    </div>
                </t>
            </templates>
        </kanban>
    </field>
</record>

Sept éléments à comprendre :

  • default_group_by="state" — définit les colonnes. Chaque valeur de state devient une colonne, déplacer une carte change automatiquement le champ.
  • <progressbar field="kanban_state"> — barre en haut de chaque colonne agrégeant les valeurs de kanban_state, avec couleurs Bootstrap mappées par le dict colors.
  • <t t-name="card">nom obligatoire en Odoo 19 (le nom kanban-box des versions antérieures est supprimé).
  • widget="priority" — transforme un Selection à 4 valeurs (0..3) en étoiles cliquables.
  • widget="state_selection" — transforme un Selection à 3 valeurs (normal/done/blocked) en rond cliquable (gris/vert/rouge).
  • t-if="record.category_id.raw_value" — affiche le séparateur "·" uniquement si la catégorie est renseignée. record est le record du kanban, .raw_value donne la valeur brute (id pour un Many2one).
  • t-attf-class — classe dynamique (attf = attribute formatted) évaluée via #{expression}. Permet de mettre la pastille deadline en rouge si is_overdue.

3. Mettre le Kanban par défaut dans l'action

On met kanban en première position dans view_mode pour que l'action l'ouvre par défaut. On en profite pour ajouter graph et pivot :

<record id="action_helpdesk_ticket" model="ir.actions.act_window">
    <field name="name">Tickets</field>
    <field name="res_model">helpdesk.ticket</field>
    <field name="view_mode">kanban,list,form,graph,pivot</field>
    <field name="search_view_id" ref="view_helpdesk_ticket_search"/>
    <field name="context">{'search_default_filter_new': 1}</field>
</record>

L'ordre dans view_mode définit à la fois la vue par défaut (la première) et l'ordre des boutons de switch en haut à droite.

4. Les widgets kanban les plus utiles

WidgetType de champUsage
prioritySelection 0→3Étoiles cliquables directement depuis la carte.
state_selectionSelection 3 valeurs (normal/done/blocked)Pastille ronde verte/grise/rouge.
many2one_avatar_userMany2one res.usersAvatar compact au lieu du nom.
many2many_tagsMany2manyPuces colorées ; option color_field pour le coloriage.
imageImageMiniature redimensionnée.
percentpieInteger/Float (0-100)Donut de progression.
boolean_toggleBooleanInterrupteur on/off.
handleIntegerHandle de drag pour sequence (vue list).

5. La vue <graph>

Trois lignes et tu as un graphique interactif : barres empilées par catégorie et par état.

<record id="view_helpdesk_ticket_graph" model="ir.ui.view">
    <field name="name">helpdesk.ticket.graph</field>
    <field name="model">helpdesk.ticket</field>
    <field name="arch" type="xml">
        <graph string="Analyse tickets" type="bar" stacked="1">
            <field name="category_id"/>
            <field name="state"/>
        </graph>
    </field>
</record>
Graph en barres empilées par catégorie et par état
Barres empilées — une couleur par état (Nouveau bleu, En cours vert, Résolu rouge). L'utilisateur peut switcher en ligne / camembert via la barre d'outils.

Options notables :

  • type="bar" / "line" / "pie" — type par défaut.
  • stacked="1" — empile plusieurs <field> dans une même barre.
  • Le premier <field> devient l'axe X, les suivants sont des mesures. Pour agréger une mesure numérique (sum, avg…), ajouter type="measure".

6. La vue <pivot>

<pivot string="Pivot tickets">
    <field name="category_id" type="row"/>
    <field name="state"       type="col"/>
    <field name="hours_spent" type="measure"/>
</pivot>
Tableau croisé dynamique catégorie x état avec mesure heures passées
Tableau croisé : catégories en lignes, états en colonnes, heures passées comme mesure agrégée (somme par défaut). Cliquable pour exploser les niveaux et exportable en Excel.

Les trois type essentiels :

  • row — le champ devient une ligne du tableau.
  • col — le champ devient une colonne.
  • measure — le champ est agrégé (sum, avg, count) dans les cellules.
Astuce — une même vue pivot peut empiler plusieurs row pour faire du drill-down (ex: catégorie puis sous-catégorie), ou plusieurs measure pour afficher côte à côte heures et coût.

Migration Kanban v17 → v19 — les changements

v17 (et avant)v19
<t t-name="kanban-box"><t t-name="card">
t-esc="value"t-out="value"
attrs="{'invisible': [...]}"invisible="state == 'done'"
<div class="o_kanban_record_top"> + bloc Bootstrap manuel Utilitaires Bootstrap 5 directement (d-flex, justify-content-between…)
Menu d'actions manuel (kanban-menu) Automatique dans Odoo 19 — les actions du bouton trois-points viennent de cog

Upgrade et test visuel

./odoo-bin -c config/odoo.conf -u odooskills_helpdesk -d ta_base --stop-after-init

# Démarre Odoo normalement, puis :
# Menu Helpdesk → Tickets → la vue Kanban s'ouvre par défaut.
# - Glisse une carte d'une colonne à l'autre → state change.
# - Clique sur les étoiles → priority change sans ouvrir la form.
# - Clique sur la pastille d'état kanban → cycle normal/done/blocked.
# - Switch view (top-right) → graph puis pivot.

Les pièges Kanban à éviter

  1. Utiliser <t t-name="kanban-box"> — renommé card en Odoo 19, le kanban reste vide sinon.
  2. Oublier <field name="champ"/> avant record.champ.raw_value — un champ non déclaré n'est pas chargé côté client, raw_value renvoie undefined.
  3. Surcharger priority avec selection= alors qu'il est hérité — warning à chaque chargement. Utiliser selection_add.
  4. Mettre type="measure" sur un champ non numérique dans pivot/graph — l'agrégation échoue silencieusement.
  5. Oublier group_edit="false" quand tu ne veux pas que l'utilisateur renomme les colonnes (valeurs fixes du Selection).

Voir aussi dans cette série

T14 — Hiérarchie de modèles

parent_id, _parent_store

T15 — Méthodes de modèle

create, write, actions

T16 — Vues Form, List, Search

backend, menus, widgets

Prochain article — T18

On passe à l'héritage de vues : ajouter des champs à une vue existante avec xpath, position="after", replace, sans casser la vue parente.

Télécharger le guide technique Odoo 19 (PDF gratuit)
Vues Form, List et Search en Odoo 19 : actions, menus et widgets
Bloc 4 · Interface utilisateur — Article 1/4 Vues Form, List et Search en Odoo 19 Donner enfin un backend à ton module helpdesk — vue liste, formulaire avec…