Se rendre au contenu

Gérer les sorties d'impression dans Odoo 19

Série Rapports PDF · Article 3/5 — Développement Odoo
28 juin 2026 par
Gérer les sorties d'impression dans Odoo 19
| Aucun commentaire pour l'instant

Série Rapports PDF · Article 3/5

Gérer les sorties d'impression dans Odoo 19

Un rapport existe : encore faut-il décider il apparaît, pour qui, quand, et comment l'imprimer en lot. Tout cela se règle sur l'action ir.actions.report, sans toucher au template ni aux vues.

~9 minutes de lecture · niveau intermédiaire · parcours Web & UI

Ce que tu vas apprendre

Brancher et débrancher

Comment binding_model_id place une sortie dans le menu Imprimer, et comment l'activer ou la retirer à volonté.

Restreindre & conditionner

Limiter une sortie à un groupe d'utilisateurs et ne l'afficher que sur les enregistrements qui matchent un domaine.

Imprimer en lot

Sélectionner plusieurs lignes dans une liste et sortir un seul PDF multi-pages, avec un nom de fichier dynamique.

Le template dessine, l'action distribue

Un rapport QWeb a deux faces. Le template décide à quoi ressemble le document ; l'action de rapport décide de sa distribution — le point d'entrée par lequel l'utilisateur déclenche l'impression. Cette seconde face est souvent négligée, alors qu'elle pèse autant sur l'expérience : une sortie mal placée ne sera jamais trouvée, une sortie trop visible polluera le menu de tout le monde.

Tout se joue sur quelques champs de l'ir.actions.report — la même action qu'on créait de zéro à l'article précédent, ou qu'on retouchait par héritage xpath au tout début de la série. Pour illustrer, on branche une fiche de suivi sur la commande de vente : réservée aux vendeurs, visible uniquement sur les commandes confirmées, et imprimable par lot.

ir.actions.report binding_model_id
group_ids
POUR QUI
domain
QUAND
docs + multi
EN LOT

1 — Où la sortie apparaît : le binding

Trois champs gouvernent l'emplacement de la sortie. binding_model_id rattache l'action à un modèle ; binding_type distingue le menu Imprimer (valeur report) du menu Action (action) ; binding_view_types précise les vues concernées — liste, fiche, ou les deux.

ChampRôle
binding_model_idLe modèle sur lequel la sortie se branche (référence à sale.model_sale_order). Vide = la sortie existe mais n'apparaît dans aucun menu.
binding_typereport pour le menu Imprimer (le défaut d'un rapport), action pour le menu Action.
binding_view_typeslist,form par défaut. Restreindre à list réserve la sortie à la sélection multiple.
<record id="action_report_order_followup" model="ir.actions.report">
    <field name="name">Fiche de suivi</field>
    <field name="model">sale.order</field>
    <field name="report_type">qweb-pdf</field>
    <field name="report_name">odooskills_print_output_demo.report_order_followup</field>
    <field name="binding_model_id" ref="sale.model_sale_order"/>
    <field name="binding_type">report</field>
    <field name="binding_view_types">list,form</field>
</record>

Module installé, une entrée Fiche de suivi rejoint le sous-menu Imprimer de la commande, aux côtés des rapports natifs Devis en PDF et Devis / Commande. Aucun bouton codé, aucune vue héritée. La liste complète des champs de l'action est documentée dans la référence officielle des rapports Odoo 19.

Menu Imprimer d'une commande de vente Odoo 19 affichant la nouvelle sortie Fiche de suivi à côté des rapports natifs
La sortie Fiche de suivi ajoutée au menu Imprimer de la commande, par le seul binding_model_id.

2 — Pour qui et quand : groupe et domaine

Une sortie n'a pas à être visible par tout le monde, ni sur tous les enregistrements. Deux champs affinent cela. group_ids limite l'entrée aux utilisateurs d'un groupe ; domain ne l'affiche que sur les enregistrements qui matchent une condition. Ici, réservée aux vendeurs et aux seules commandes confirmées (state == 'sale') :

    <field name="group_ids" eval="[(4, ref('sales_team.group_sale_salesman'))]"/>
    <field name="domain">[('state', '=', 'sale')]</field>

Sur un devis encore en brouillon, la sortie reste masquée ; elle n'apparaît qu'une fois la commande confirmée. Et un utilisateur hors du groupe ventes ne la verra jamais, même sur une commande éligible. Le domain évite ainsi d'imprimer un document qui n'aurait pas de sens dans l'état courant — un garde-fou métier posé sans une ligne de Python.

3 — Activer ou retirer la sortie

Le branchement n'est pas figé dans le marbre du XML. Une sortie d'impression s'active ou se retire à la demande, car le binding n'est rien d'autre que la valeur de binding_model_id. Odoo expose deux méthodes pour la piloter :

# Brancher la sortie sur le menu Imprimer du modèle
report = self.env.ref('odooskills_print_output_demo.action_report_order_followup')
report.create_action()      # écrit binding_model_id  → l'entrée apparaît

# La retirer du menu (le rapport reste appelable par URL ou par code)
report.unlink_action()      # vide binding_model_id   → l'entrée disparaît

Côté interface, ces deux méthodes correspondent aux boutons Ajouter dans le menu « Imprimer » et Retirer du menu « Imprimer », sur la fiche du rapport dans Paramètres → Technique → Rapports. Retirer le binding ne supprime pas le rapport : il disparaît du menu mais reste accessible par son URL /report/pdf/<action>/<id> ou par appel direct. C'est la façon propre de désactiver une sortie sans rien casser : on débranche, on rebranche, l'historique du template est intact.

4 — Imprimer plusieurs documents d'un coup

Le menu Imprimer apparaît aussi en haut d'une liste dès qu'on coche plusieurs lignes. Déclencher la sortie sur trois commandes produit alors un seul PDF de trois pages — à condition que le template sache boucler. C'est précisément le rôle de docs, le recordset que le moteur de rapport passe au template : il contient tous les enregistrements sélectionnés. Ce mécanisme de boucle est au cœur du moteur QWeb ; on le détaille dans les fondamentaux des rapports QWeb.

Liste des commandes de vente Odoo 19 avec trois lignes sélectionnées et le menu Imprimer ouvert sur la sortie Fiche de suivi
Trois commandes cochées : le menu Imprimer propose la sortie pour l'ensemble de la sélection.

Le template enveloppe le contenu dans html_container, boucle sur docs avec t-foreach, et chaque itération obtient son en-tête via external_layout. Le print_report_name, lui, calcule un nom de fichier lisible à partir de l'enregistrement.

<field name="print_report_name">'Suivi - %s' % (object.name)</field>

<template id="report_order_followup">
    <t t-call="web.html_container">
        <t t-foreach="docs" t-as="doc">
            <t t-call="web.external_layout">
                <div class="page">
                    <h2>Suivi de commande <span t-field="doc.name"/></h2>
                    <table class="table table-sm mt-4">
                        <tr t-foreach="doc.order_line" t-as="line">
                            <td><span t-field="line.product_id"/></td>
                            <td class="text-end"><span t-field="line.product_uom_qty"/></td>
                            <td class="text-end"><span t-field="line.price_subtotal"/></td>
                        </tr>
                    </table>
                </div>
            </t>
        </t>
    </t>
</template>

Version condensée pour la lisibilité : le module complet ajoute le bloc client/statut, les en-têtes de colonnes (thead) et la ligne de total doc.amount_total, visibles sur le PDF ci-dessous.

Une seule sortie, trois commandes, trois pages enchaînées dans le même document — chacune avec son en-tête et son pied de page société :

PDF unique généré par Odoo 19 contenant trois fiches de suivi de commande sur trois pages, chacune avec en-tête société, tableau d'articles et total
Les trois commandes cochées imprimées en un seul PDF : la boucle docs enchaîne les pages.

⚠️ Trois pièges sur la distribution

  • group_ids, pas groups_id. Le champ de restriction par groupe a été renommé en v19. Un ancien module qui écrit groups_id sur l'action plante à l'installation.
  • Pas de t-field directement sur un <td>. Le rendu lève QWeb widgets do not work correctly on 'td' elements. On enveloppe la valeur dans un <span t-field="…"/> à l'intérieur de la cellule.
  • Le domain filtre l'affichage, pas l'autorisation. Il masque la sortie dans le menu, mais l'URL du rapport reste atteignable ; pour une vraie restriction d'accès, c'est group_ids qui fait foi.

À retenir

🔌 Le binding place la sortie

binding_model_id + binding_view_types décident du modèle et des vues où l'entrée Imprimer apparaît.

🎯 Groupe + domaine ciblent

group_ids pour qui, domain pour quand — deux champs, zéro Python.

🔁 create / unlink_action

Brancher ou retirer une sortie à la volée sans supprimer le rapport.

📚 docs imprime en lot

Boucler sur docs sort un PDF unique multi-pages depuis une sélection.

Télécharge le Guide Technique Odoo

Un module Odoo 19 fonctionnel, 20+ articles techniques, environnements de dev et pipeline complet — le tout en PDF.

Télécharger le guide

À lire également

↔ Le pendant fonctionnel

Tu préfères régler tes sorties d'impression sans coder ? Le blog fonctionnel couvre la même mécanique côté configuration : modèles de rapports et format d'impression dans Odoo 19 Community.

Se connecter pour laisser un commentaire.
Créer son rapport PDF de A à Z dans Odoo 19
Série Rapports PDF · Article 2/5 — Développement Odoo