Se rendre au contenu

Créer son rapport PDF de A à Z dans Odoo 19

Série Rapports PDF · Article 2/5 — Développement Odoo
28 juin 2026 par
Créer son rapport PDF de A à Z dans Odoo 19
| Aucun commentaire pour l'instant

Série Rapports PDF · Article 2/5

Créer son rapport PDF de A à Z dans Odoo 19

Pas une retouche, un rapport entièrement neuf. On construit une fiche contact imprimable à partir de rien : un format papier dédié, une action ir.actions.report, un template QWeb. À la fin, une nouvelle entrée apparaît dans le menu Imprimer du contact, et elle sort un PDF.

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

Ce que tu vas apprendre

Les trois ingrédients

Un rapport PDF Odoo tient en trois objets : un format papier, une action et un template. Ni plus, ni moins.

Le branchement au menu

Comment binding_model_id fait apparaître ton rapport dans le menu Imprimer du bon modèle, sans toucher aux vues.

Le squelette QWeb

L'enchaînement html_containerexternal_layoutpage qui donne en-tête et pied de page société gratuitement.

Un rapport, trois objets

Créer un rapport de zéro intimide souvent. Pourtant, Odoo n'attend que trois pièces, et elles se déclarent toutes en XML :

report.paperformat
le format papier
ir.actions.report
l'action + le menu
template QWeb
le contenu

Le format papier fixe les marges et l'orientation. L'action relie un modèle, un template et ce format ; c'est elle qui décide où le bouton d'impression apparaît. Le template dessine le document. On les pose dans cet ordre. Notre exemple : une fiche contact imprimable depuis n'importe quelle société ou personne.

Étape 1 — Le format papier

Un report.paperformat décrit la feuille : taille, orientation, marges en millimètres, espacement de l'en-tête. On en crée un dédié plutôt que de réutiliser celui d'Odoo, pour garder la main sur les marges de notre fiche.

<record id="paperformat_contact_sheet" model="report.paperformat">
    <field name="name">Fiche contact A4</field>
    <field name="format">A4</field>
    <field name="orientation">Portrait</field>
    <field name="margin_top">40</field>
    <field name="margin_bottom">25</field>
    <field name="margin_left">10</field>
    <field name="margin_right">10</field>
    <field name="header_spacing">35</field>
    <field name="dpi">90</field>
</record>

Rien d'obligatoire ici : sans format dédié, Odoo prend celui de la société. Mais dès qu'on veut des marges précises, ce petit enregistrement fait la différence. Côté configuration, le format d'impression se règle aussi sans code.

Étape 2 — L'action de rapport

Le cœur du dispositif. L'ir.actions.report relie tout : le modèle visé (res.partner), le template à rendre, le format papier. Et surtout, son binding_model_id est ce qui fait surgir l'entrée dans le menu d'impression du contact.

<record id="action_report_contact_sheet" model="ir.actions.report">
    <field name="name">Fiche contact</field>
    <field name="model">res.partner</field>
    <field name="report_type">qweb-pdf</field>
    <field name="report_name">odooskills_contact_report.report_contact_sheet</field>
    <field name="report_file">odooskills_contact_report.report_contact_sheet</field>
    <field name="print_report_name">'Fiche - %s' % (object.name)</field>
    <field name="paperformat_id" ref="paperformat_contact_sheet"/>
    <field name="binding_model_id" ref="base.model_res_partner"/>
    <field name="binding_type">report</field>
</record>

Trois champs méritent l'attention. report_name doit être préfixé du nom technique du module (module.template) : c'est l'identifiant complet du template, pas son seul id. print_report_name calcule le nom du fichier téléchargé à partir de l'enregistrement (object). binding_model_id, enfin, pointe vers l'identifiant externe du modèle — base.model_res_partner — et c'est lui qui branche le rapport sur le menu.

Résultat immédiat : dans le menu d'actions d'une fiche contact, une entrée Fiche contact est apparue. Aucun bouton à coder, aucune vue à modifier.

Menu d'actions d'un contact Odoo 19 affichant la nouvelle entrée d'impression Fiche contact
L'entrée Fiche contact ajoutée au menu d'actions du contact, par le seul binding_model_id.

Étape 3 — Le template QWeb

Le contenu, enfin. Un template de rapport suit toujours le même squelette : web.html_container enveloppe le tout, on boucle sur les enregistrements (docs), et web.external_layout apporte gratuitement l'en-tête et le pied de page de la société. Notre contenu vit dans le <div class="page">. Ce squelette est le même pour tous les rapports ; on le retrouve en détail dans les fondamentaux du moteur QWeb.

<template id="report_contact_sheet">
    <t t-call="web.html_container">
        <t t-foreach="docs" t-as="doc">
            <t t-call="web.external_layout">
                <div class="page">
                    <h2 t-field="doc.name"/>
                    <div t-if="doc.function" class="text-muted" t-field="doc.function"/>

                    <div class="row mt-3">
                        <div class="col-6">
                            <strong>Adresse</strong>
                            <div t-if="doc.street" t-field="doc.street"/>
                            <div><span t-field="doc.zip"/> <span t-field="doc.city"/></div>
                            <div t-if="doc.country_id" t-field="doc.country_id"/>
                        </div>
                        <div class="col-6">
                            <strong>Coordonnées</strong>
                            <div t-if="doc.phone">Tél. : <span t-field="doc.phone"/></div>
                            <div t-if="doc.email">Courriel : <span t-field="doc.email"/></div>
                            <div t-if="doc.website">Site : <span t-field="doc.website"/></div>
                        </div>
                    </div>

                    <div class="row mt-4">
                        <div class="col-6" t-if="doc.parent_id">
                            <strong>Société</strong>
                            <div t-field="doc.parent_id.name"/>
                        </div>
                        <div class="col-6" t-if="doc.vat">
                            <strong>N° TVA</strong>
                            <div t-field="doc.vat"/>
                        </div>
                    </div>

                    <div class="mt-4" t-if="doc.category_id">
                        <strong>Étiquettes : </strong>
                        <span t-foreach="doc.category_id" t-as="tag" class="me-1">
                            <span class="badge text-bg-secondary" t-field="tag.name"/>
                        </span>
                    </div>
                </div>
            </t>
        </t>
    </t>
</template>

La variable de boucle s'appelle doc (on itère sur docs, fourni automatiquement par le moteur de rapport). Chaque champ s'affiche avec t-field, qui gère le formatage et la traduction. Une fois le module installé, cliquer sur Fiche contact produit ce PDF, en-tête société comprise :

Fiche contact PDF générée par le rapport QWeb personnalisé dans Odoo 19, avec en-tête société, adresse, coordonnées et N° TVA
Le PDF produit : en-tête et pied via external_layout, contenu de la fiche dans la page.

⚠️ Quatre pièges qui font échouer le rendu

  • html_container est obligatoire. Sans cette enveloppe, le PDF ne charge ni les styles ni la structure de page. C'est la première chose à vérifier devant une page blanche.
  • report_name = module.template. Oublier le préfixe du module donne un rapport « introuvable » à l'impression.
  • t-field exige un point. t-field="doc" lève une erreur : il faut toujours un champ, comme doc.name. Pour afficher l'enregistrement lui-même, on liste ses champs un à un.
  • Le champ mobile a disparu de res.partner en v19. Reprendre un vieux template qui l'utilise plante le rendu ; on s'appuie désormais sur phone.

À retenir

🧱 Trois objets suffisent

Format papier, action, template : un rapport complet sans une ligne de Python.

🖨️ Le menu vient du binding

binding_model_id + binding_type="report" branchent le rapport sur le menu d'impression du modèle.

🏛️ external_layout offert

En-tête et pied de page société sont fournis ; ton contenu vit dans la page.

🔖 Nomme bien le template

report_name doit être préfixé du module — l'erreur n°1 du débutant.

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

Se connecter pour laisser un commentaire.
Personnaliser un rapport PDF existant dans Odoo 19
Série Rapports PDF · Article 1/5 — Développement Odoo