# myFAB Rapport docx

# myFAB · Rapport DOCX — Référence rapide

> **Prérequis** : `pip install docxtpl docxcompose htmldocx` · LibreOffice requis pour le mode PDF

---

## Créer un paramétrage

**Technique → Rapport DOCX → Configurations → Nouveau**

<table id="bkmrk-champ-valeur-attendu"><thead><tr><th>Champ</th><th>Valeur attendue</th><th align="center">Obligatoire</th></tr></thead><tbody><tr><td>Nom du rapport</td><td>Libellé affiché dans l'interface</td><td align="center">✓</td></tr><tr><td>Code rapport</td><td>Identifiant technique unique, sans espaces — ex : `declaration_conformite_ce`</td><td align="center">✓</td></tr><tr><td>Modèle</td><td>Modèle Odoo cible (ex : `sale.order`, `mrp.production`)</td><td align="center">✓</td></tr><tr><td>Champ nom fichier</td><td>Champ `Char` du modèle utilisé pour nommer le fichier</td><td align="center">✓</td></tr><tr><td>Template DOCX</td><td>Fichier `.docx` avec balises Jinja2</td><td align="center">✓</td></tr><tr><td>Préfixe</td><td>Texte ajouté au début du nom de fichier généré</td><td align="center"> </td></tr><tr><td>Mode de fusion</td><td>`composer` (DOCX unique) · `zip` (un fichier/enregistrement) · `pdf` (via LibreOffice)</td><td align="center">✓</td></tr><tr><td>Nom fichier impression</td><td>Formule Jinja — ex : `{{ docs.name }}`</td><td align="center"> </td></tr><tr><td>Autoescape</td><td>Activer si le template contient `<`, `>` ou `&`</td><td align="center"> </td></tr></tbody></table>

> Publier via le bouton **Publier** — tous les champs passent en lecture seule.  
> Pour modifier : **Dépublier** d'abord.

---

## Syntaxe du template

### Champs

```
{{docs.nom_du_champ}}
{{docs.relation_id.champ}}

```

### Fonctions utilitaires

<table id="bkmrk-fonction-description"><thead><tr><th>Fonction</th><th>Description</th></tr></thead><tbody><tr><td>`{{spelled_out(docs.champ)}}`</td><td>Nombre en toutes lettres</td></tr><tr><td>`{{formatdate(docs.champ)}}`</td><td>Date formatée (Babel, `fr_FR` par défaut)</td></tr><tr><td>`{{fdate(docs.champ, "%d/%m/%Y")}}`</td><td>Date avec format personnalisé</td></tr><tr><td>`{{fnum(docs.champ, " ", ",", 2)}}`</td><td>Numérique — `fnum(val, sep_milliers, sep_décimal, décimales)`</td></tr><tr><td>`{{parsehtml(docs.champ)}}`</td><td>HTML → texte brut</td></tr><tr><td>`{{p html2docx(docs.champ)}}`</td><td>HTML → sous-document Word</td></tr><tr><td>`{{convert_currency(docs.champ, docs.currency_id)}}`</td><td>Montant avec devise</td></tr><tr><td>`{{render_image(docs.champ, width=30, height=30)}}`</td><td>Image binaire (mm)</td></tr><tr><td>`{{r rich_text(docs.champ)}}`</td><td>Texte enrichi</td></tr><tr><td>`{{p add_subdoc(docs.champ)}}`</td><td>Sous-document DOCX binaire</td></tr><tr><td>`{{replace_image('nom', docs.champ)}}`</td><td>Remplacement d'image dans le template</td></tr></tbody></table>

> Langue surchargeable par fonction : `{{formatdate(docs.champ, lang='en_US')}}`

---

## Boucles dans un tableau

Chaque directive `{%tr %}` doit occuper **une ligne entière** du tableau, avec une cellule fusionnée sur toute la largeur.

```
{%tr for line in docs.order_line_ids %}
  {{line.product_id.display_name}}  |  {{line.qty}}  |  {{line.price_unit}}
{%tr endfor %}

```

- Les lignes `for` et `endfor` sont supprimées du rendu final.
- Les champs s'accèdent via la variable de boucle (`line`).

---

## Conditions dans un tableau

Utiliser **impérativement** `if / else / endif` — deux blocs `if` séparés provoquent une `TemplateSyntaxError`.

```
{%tr for line in docs.order_line_ids %}
{%tr if not line.uom_qty %}
  [cellule fusionnée] {{line.product_id.display_name}}   ← ligne de section
{%tr else %}
  {{line.product_id.display_name}}  |  {{line.uom_qty}}  |  {{line.price_unit}}
{%tr endif %}
{%tr endfor %}

```

**Ordre obligatoire :** `for` → `if` → `else` → `endif` → `endfor`

> Préférer `not line.champ` à `line.champ == 0` pour couvrir `0`, `0.0`, `False` et `None`.

> La directive `{%tr if %}` et le contenu `{{champ}}` doivent être dans deux `<w:r>` séparés dans le XML Word — sinon le contenu ne s'affiche pas.

---

## Mode PDF

Configurer le chemin LibreOffice dans **Paramètres → Technique → Paramètres système** :

<table id="bkmrk-os-valeur-linux-%2Fusr"><thead><tr><th>OS</th><th>Valeur</th></tr></thead><tbody><tr><td>Linux</td><td>`/usr/bin/libreoffice`</td></tr><tr><td>Windows</td><td>`C:\Program Files\LibreOffice\program\soffice.exe`</td></tr></tbody></table>

Clé : `default_libreoffice_path`

---

## Erreurs courantes

<table id="bkmrk-erreur-cause-solutio"><thead><tr><th>Erreur</th><th>Cause</th><th>Solution</th></tr></thead><tbody><tr><td>`TemplateSyntaxError: unknown tag 'endif'`</td><td>Deux blocs `if` séparés dans un `for`</td><td>Remplacer par `if / else / endif`</td></tr><tr><td>Ligne fusionnée vide</td><td>`{%tr if %}` et `{{champ}}` dans le même `<w:r>`</td><td>Les séparer en deux runs distincts</td></tr><tr><td>Condition `== 0` ignorée</td><td>Le champ est un `float` (`0.0 != 0`)</td><td>Utiliser `not line.champ`</td></tr><tr><td>Erreur 500 générique</td><td>Erreur Python non affichée</td><td>`journalctl -u odoo -n 100` ou `tail -f /var/log/odoo/odoo.log`</td></tr><tr><td>PDF non généré</td><td>LibreOffice absent ou mal configuré</td><td>Installer + configurer `default_libreoffice_path`</td></tr></tbody></table>

## Exemple action serveur pour générer une rapport et le mettre en pièce jointe directement d'un OF

> report\_name = 'DOCX-OF'
> 
> report = env\['ir.actions.report'\].\_get\_report\_from\_name(report\_name)
> 
> if not report:  
>  raise UserError(  
>  "Le rapport DOCX '%s' est introuvable." % report\_name  
>  )
> 
>   
> file\_content, file\_extension = report.\_render\_docx(  
>  report\_name,  
>  \[object.id\],  
>  data={}  
> )
> 
>   
> if file\_extension != 'docx':  
>  raise UserError(  
>  "Le rapport retourne '%s' au lieu de 'docx'."  
>  % file\_extension  
>  )
> 
>   
> document = env\['document.openprod'\].create({  
>  'name': 'Rapport DOCX - %s' % object.name,  
>  'extension': 'docx',  
>  'document\_type': 'document',  
>  'attachment': b64encode(file\_content),  
>  'directory\_id': env.ref(  
>  'base\_openprod.default\_document\_directory'  
>  ).id,  
>  'user\_id': env.user.id,  
>  'company\_id': object.company\_id.id or env.company.id,  
>  'date': datetime.date.today(),
> 
>   
>  # Liaison avec l'OF  
>  'internal\_plan\_ids': \[(4, object.id)\],  
> })

<div id="bkmrk--6"></div>