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.
sale installé, et un
module custom. On reste en déclaratif XML, avec un court détour Python pour le
branchement à la volée. Si tu n'as pas encore créé de rapport, commence par
l'article
précédent qui en construit un de zéro.
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.
OÙ 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.
| Champ | Rôle |
|---|---|
binding_model_id | Le 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_type | report pour le menu Imprimer (le défaut d'un rapport), action pour le menu Action. |
binding_view_types | list,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.
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.
group_ids qui
porte la restriction. L'ancien groups_id a été renommé ; un module
repris d'une version antérieure échouera à l'installation sur ce point.
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.
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é :
docs enchaîne les pages.⚠️ Trois pièges sur la distribution
group_ids, pasgroups_id. Le champ de restriction par groupe a été renommé en v19. Un ancien module qui écritgroups_idsur l'action plante à l'installation.- Pas de
t-fielddirectement sur un<td>. Le rendu lèveQWeb widgets do not work correctly on 'td' elements. On enveloppe la valeur dans un<span t-field="…"/>à l'intérieur de la cellule. - Le
domainfiltre 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'estgroup_idsqui 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📘 Pour aller plus loin : nos formations Odoo 19
À lire également
- Créer son rapport PDF de A à Z — le rapport qu'on distribue ici.
- Personnaliser un rapport existant (xpath) — retoucher plutôt que créer.
- Côté métier : modèles de rapports et format d'impression — gérer les sorties sans coder.
↔ 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.