Se rendre au contenu

Personnaliser un rapport PDF existant dans Odoo 19

Série Rapports PDF · Article 1/5 — Développement Odoo
28 juin 2026 par
Personnaliser un rapport PDF existant dans Odoo 19
| Aucun commentaire pour l'instant

Série Rapports PDF · Article 1/5

Personnaliser un rapport PDF existant dans Odoo 19

Ajouter une mention, une échéance ou un bloc de coordonnées bancaires sur la facture standard — sans réécrire le rapport. La bonne méthode tient en un mot : l'héritage QWeb par xpath. On l'applique ici à la facture, puis on montre que le même geste vaut pour le devis, la demande de prix et le bon de commande fournisseur.

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

Ce que tu vas apprendre

Hériter, pas copier

Cibler un nœud précis du rapport standard avec xpath et y greffer ton contenu — le reste du modèle officiel continue d'évoluer avec Odoo.

Les trois positions clés

before, after et attributes : trois façons d'insérer ou de modifier, illustrées sur une vraie facture.

Un geste réutilisable

Le même schéma s'applique au devis, à la demande de prix et au bon de commande fournisseur. Un tableau de correspondance te donne les bons identifiants.

Un rapport PDF n'est qu'une vue QWeb

Derrière chaque bouton Imprimer se cache une action ir.actions.report qui pointe vers un template QWeb. Pour la facture client, ce template s'appelle account.report_invoice_document. Odoo le rend en HTML, puis le convertit en PDF. (Quand c'est un classeur qu'il te faut plutôt qu'un PDF, générer un rapport Excel natif répond à l'autre moitié du besoin.)

La tentation du débutant : copier ce template entier dans son module pour le modifier. Mauvaise idée. À la moindre mise à jour d'Odoo, ta copie diverge du modèle officiel et tu rates les corrections. La bonne approche consiste à hériter le template et à n'y décrire que ta modification. À l'inverse, bâtir un rapport QWeb de toutes pièces repart, lui, d'un template vierge — c'est un autre chantier.

Template standard
report_invoice_document
+ Vue héritée
inherit_id + xpath
Rapport PDF final

Une vue héritée déclare l'inherit_id du template à étendre, puis une ou plusieurs balises <xpath>. Chaque xpath désigne un nœud du template d'origine et une position qui dit où insérer : avant, après, à l'intérieur, ou bien en remplaçant ses attributs.

Le cas pilier : la facture client

Objectif concret : faire ressortir l'échéance de paiement avant le tableau des lignes, et ajouter un encadré « coordonnées bancaires » après les totaux. On touche ici au contenu ; pour la mise en page globale (marges, format papier), le format d'impression se règle sans code. Voici la facture standard, telle qu'Odoo la produit par défaut.

Facture PDF standard d'Odoo 19 avant personnalisation, sans bloc d'échéance ni coordonnées bancaires
La facture standard : en-tête société, lignes, totaux. Rien de plus.

Et voici la même facture, une fois le module de personnalisation installé : un bandeau d'échéance apparaît au-dessus des lignes, et un encadré de mentions se loge sous les totaux. Le corps officiel du rapport, lui, n'a pas bougé d'un pixel.

Même facture PDF après héritage QWeb : bandeau d'échéance de paiement et encadré coordonnées bancaires ajoutés
Après héritage : échéance mise en avant, encadré bancaire ajouté. Deux greffes, zéro réécriture.

Le code : trois positions xpath

Tout tient dans une seule vue héritée. On déclare l'inherit_id, puis on enchaîne les xpath. Remarque la variable de boucle : dans le rapport de facture, l'enregistrement courant s'appelle o.

<template id="report_invoice_document_oski"
          inherit_id="account.report_invoice_document">

    <!-- 1) position="before" : un bandeau juste AVANT le tableau des lignes -->
    <xpath expr="//table[@name='invoice_line_table']" position="before">
        <div t-if="o.move_type == 'out_invoice' and o.invoice_date_due"
             style="border-left:3px solid #d6336c;background:#fdf2f6">
            <span class="fw-bold">Échéance de paiement :</span>
            <span t-field="o.invoice_date_due"/>
        </div>
    </xpath>

    <!-- 2) position="attributes" : on AJOUTE une classe au tableau existant -->
    <xpath expr="//table[@name='invoice_line_table']" position="attributes">
        <attribute name="class" add="oski-lines" separator=" "/>
    </xpath>

    <!-- 3) position="after" : un encadré APRÈS le bloc des totaux -->
    <xpath expr="//div[@id='total']" position="after">
        <div style="border-top:3px solid #d6336c;background:#fbfbfc">
            <p class="fw-bold">Merci de votre confiance.</p>
            <p t-if="o.partner_bank_id">
                <span class="fw-bold">IBAN :</span>
                <span t-field="o.partner_bank_id.acc_number"/>
            </p>
        </div>
    </xpath>

</template>

Trois techniques, trois usages. before et after insèrent un bloc relativement à un nœud existant. attributes ne crée rien : il modifie un attribut du nœud visé — ici on ajoute une classe sans écraser celles d'origine, grâce à add= et separator=" ".

Comment choisir le nœud cible ? On lit le template standard et on s'accroche à un point stable : un id (comme id="total") ou un attribut name (comme name="invoice_line_table"). Ces ancres sont pensées pour l'héritage et changent rarement d'une version à l'autre — bien plus sûres qu'un chemin reposant sur la position d'un <div>.

Côté manifeste, aucune dépendance exotique : on déclare les modules dont on étend les rapports, et le fichier de vues.

{
    'name': 'Personnalisation rapports PDF',
    'version': '19.0.1.0.0',
    'depends': ['account', 'sale', 'purchase'],
    'data': ['report/account_invoice_inherit.xml'],
    'license': 'LGPL-3',
}

⚠️ Le piège qui fait perdre une heure : o ou doc ?

La variable qui représente l'enregistrement courant n'est pas la même selon le rapport. Reprendre un xpath d'un rapport à l'autre sans vérifier ce nom provoque un rendu vide ou une erreur QWeb :

  • Facture, bon de commande, demande de prix → variable o
  • Devis / commande de vente → variable doc

En cas de doute, ouvre le template d'origine et cherche le t-foreach ou le t-set qui définit la variable de boucle.

Le même geste, sur tous les documents

La méthode ne change pas : on hérite le bon template et on cible une ancre stable. Seuls l'inherit_id et la variable de boucle varient. Voici la table de correspondance pour les quatre rapports les plus demandés.

Documentinherit_idVariable
Facture clientaccount.report_invoice_documento
Devis / Pro formasale.report_saleorder_documentdoc
Demande de prix (RFQ)purchase.report_purchasequotation_documento
Bon de commande fournisseurpurchase.report_purchaseorder_documento

Détail utile : le rapport Pro forma de vente (sale.report_saleorder_pro_forma) réutilise en interne le même report_saleorder_document. Hériter ce dernier suffit donc à toucher le devis et la pro forma, sans une ligne de plus.

Sur le devis, le même schéma ajoute un rappel de validité de l'offre. La greffe s'accroche cette fois au bloc d'informations, avec la variable doc :

<template id="report_saleorder_document_oski"
          inherit_id="sale.report_saleorder_document">
    <xpath expr="//div[@id='informations']" position="after">
        <div t-if="doc.validity_date" style="border-left:3px solid #d6336c">
            Offre valable jusqu'au <span t-field="doc.validity_date"/>.
        </div>
    </xpath>
</template>
Devis PDF Odoo 19 avec rappel de validité de l'offre ajouté par héritage QWeb
Même technique sur le devis : le rappel de validité s'insère sous l'en-tête, ici avec la variable doc.

Quatre réflexes Odoo 19

À retenir

🎯 Hériter plutôt que copier

Un inherit_id + des xpath ciblés : ta perso survit aux mises à jour d'Odoo.

📌 Trois positions

before et after insèrent, attributes modifie un attribut existant.

🧭 Des ancres stables

Accroche-toi à un @name ou un @id du template d'origine, pas à une position fragile.

🔁 Un geste, quatre documents

Facture, devis, RFQ, bon de commande : même méthode, seul l'inherit_id change — attention à o vs doc.

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.
Industrialiser l'export Excel dans Odoo 19
Série Rapports Excel · Article 6/6 — Développement Odoo