Home / ERP/CRM : OpenERP / Admin Guide OpenERP / Customisation des documents / Rapports Open Office et RML

Rapports Open Office et RML

C'est un fichier au format OpenOffice 1.0 qui contient la mise en page et des expressions entre crochets pour apeller les données à afficher.

Template RML

Le fichier RML est généré sur la base du fichier .sxw par le script de conversion sxw2rml qui se trouve dans le module base_report_designer" (bin/addons/base_report_designer/wizard/tiny_sxw2rml/tiny_sxw2rml.py).

Serveur TinyERP

Remplace les expressions entre [] par les valeurs correspondantes prises dans la base de données.

Exemple: [o.partner_id.country_id.name] sera remplacé par le nom du pays pour le partenaire considéré.

Lancer la conversion

  1. Se placer dans le répertoire contenant le script tiny_sxw2rml.py
  2. Uploader le template OpenOffice dans votre répertoire home
  3. Lancer la conversion avec la commande ./tiny_sxw2rml.py <chemin vers .sxw> > <chemin vers .rml> (./tiny_sxw2rml.py /home/<mon user>/order.sxw > ../../../sale/report/order.rml)

Construction des rapports

TinyERP permet de définir un template applicable pour tous documents. Il est stocké dans le fichier 'corporate_rml_header.rml' du module 'custom'.

Ajouter un logo

Pour ajouter un logo dans le coin gauche de la page:

<image file="/home/<mon user>/<mon logo>" x="2cm" y="26.5cm" width="5cm" />

Ajoute sur tous les documents le logo avec:

  • Une largeur de 5 cm.
  • Une distance au bord gauche de la page A4 de 2 cm
  • Une distance au bord inférieur de la page A4 de 26.5 cm

Ce code est à insérer dans le fichier corporate_rml_header.rml du module custom/

Mise en page

Dans un template, tous les eléments du rapport sont positionné par rapport au coin bas / gauche

Un document commence par la définition de sa taille, par exemple pour un rapport A4

<header>
<pageTemplate>
<frame id="first" x1="1cm" y1="2.5cm" width="19.0cm" height="23.0cm"/>

Ensuite commence le remplissage du document avec la balise <pageGraphics>

A l'intérieur de ce bloc, il est possible de définir la mise en page avec les commandes suivantes:

<setFont name="Helvetica-Bold" size="30"/> Défini le type et la taille de la police
<fill color="darkblue"/> Défini la couleur de la police

<drawString x="2cm" y="2.2cm">

<drawCentredString x="11cm" y="1.3cm">

Place un texte dans une boîte virtuelle avec x et y comme coordonnées.

<stroke color="red"/>

<lines>1cm 27.7cm 20cm 27.7cm</lines>

Trace une ligne rouge
<pageNumber/>

Imprime le numéro de page

Comment imprimer qcq du genre 3/5 (récupérer le nombre total de page?)

Ajouter une variable

Tous les textes affichés dans les rapports correspondent à des variables passées au rapport. Chaque rapport est construit sur la base d'un objet [[o]] et des attributs de cet objet.

Par exemple, le nom du partenaire sur une facture est représenté par l'expression suivante: [[ o.partner_id.name ]]

Pour connaître les variables disponibles, il faut imprimer le guide technique du module utilisé. Les guides techniques sont disponibles depuis le client de TinyERP dans 'Administration > Gestion des modules > Modules' et cliquer sur le bouton en haut à droite 'Technical guide'.

Exemple de rapport – Purchase order

Un rapport commence toujours par l'appel à l'objet. Dans notre cas, nous chargeons un purchase.order

[[ repeatIn(objects,'o') ]]

Le menu 'Administration > Personnalisation > Bas niveau > Base > Structure de la base de données > Objets' permet de visualiser les objets et variables disponibles.

Le modèle 'purchase.order' est lié à l'objet 'purchase.order.line'

L'objet que nous venons de charger comporte un vingtaine de variables.

Les variables simple sont directement accessible dans le rapport en utilisant une syntaxe du type ci-dessous.

Valeur hors taxe = [[ o.amount_untaxed or '' ]]
Etat (sélection) = [[ o.state ]]
Date de commande = [[ o.date_order ]] ou 
[[ time.strftime('%d/%m/%Y', time.strptime(o.date_order, '%Y-%m-%d')) ]]

La relation 'many2one'

L'objet 'purchase.order' possède un attribut du type 'many2one' qui le lie au partenaire, c'est 'o.partner_id'. Cette relation nous donne accès à l'objet 'res.partner'

dont nous pouvons alors utiliser les attributs avec des notations du genre.

[[ o.partner_id.title or '' ]] [[ o.partner_id.name ]]
[[ o.partner_id.credit_limit ]]

Nous avons également accès à l'objet 'res.partner.address' dont nous pouvons utiliser les attributs

Partenaire:

 [[ o.partner_address_id.title or '' ]] [[ o.partner_address_id.name ]]

Rue:

[[ o.partner_address_id.street ]]

Pays:

[[ o.partner_address_id.country_id and o.partner_address_id.name 
or removeParentNode('para') ]]

La notation 'removeParentNode('para')' indique que si variable 'o.partner_address_id.country_id' est vide, alors toute la ligne est retirée de l'affichage (y compris le texte en début de ligne).

La construction en 'many2one' est extensible, comme l'objet 'res.partner' est relié à l'objet 'account.account' par l'attribut 'property_account_receivable' de type 'many2one', nous pouvons écrire:

Compte client =

[[ o.partner_id.property_account_receivable.name ]]

Nom du partenaire de l'adresse de livraison =

[[ o.dest_address_id.partner_id.name ]]

La relation 'one2many'

L'objet 'purchase.order' possède aussi des relations en 'one2many', par exemple l'attribut 'order_line' qui contient les lignes de la commande.

Il faut à nouveau lancer une boucle pour récupérer les valeurs de l'objet.

[[ repeatIn(o.order_line,'line') ]]

Attention, une boucle doit être placée à l'intérieur d'une section (dans Open Office, menu 'Insertion > Section') La section va jouer le rôle de délimiteur de la boucle.

Nous avons crée, pour chaque ligne de la commande un objet 'line'. Nous pouvons afficher:

[[ repeatIn(o.order_line,'line') ]]

[[ line.name ]] - [[ line.price_unit ]]

Il est possible d'accéder à des fonctions python, par exemple pour lire la référence du produit chez le partenaire. Si une référence du produit est défini pour le fournisseur, afficher cette valeur, sinon, afficher le nom du produit.

[[ line.product_id and get_product_code(line.product_id.id, o.partner_id.id) 
or line.name ]]

On retrouve la method get_product_code() dans purchase/report/order.py :

def _get_product_code(self, product_id, partner_id): 
product_obj=pooler.get_pool(self.cr.dbname).
get('product.product') 
return product_obj._product_code(self.cr, self.uid, 
[product_id], name=None, arg=None, 
context={'partner_id': partner_id})[product_id]

Cette method fait reference à une method _product_code() définit dans product/product.py :

def _product_code(self, cr, uid, ids, name, arg, context={}): 
res = {} for p in self.browse(cr, uid, ids, context):
res[p.id] = self._get_partner_code_name(cr, uid, [], p.id, 
context.get('partner_id', None), 
context)['code'] return res

L'objectif est maintenant de récuperer le contenu du champ 'product_name' dans la table product.supplierinfo. Il faut créer deux methodes supplémentaires. La premiere dans product/product.py

 def _product_name(self, cr, uid, ids, name, arg, context={}): 
res = {} for p in self.browse(cr, uid, ids, context):
res[p.id] = self._get_partner_code_name(cr, uid, [], p.id, 
context.get('partner_id', None), 
context)['name'] return res

Ensuite, dans purchase/report/order.py, on crée :

def _get_product_name(self, product_id, partner_id): product_obj=pooler.get_pool(self.cr.dbname).get('product.product') 
return product_obj._product_name(self.cr, self.uid, [product_id], 
name=None, arg=None, context={'partner_id': partner_id})[product_id]

Dans le template, nous pouvons ajouter:

[[ line.product_id and get_product_name(line.product_id.id, o.partner_id.id) or '' ]]

Il est possible de contrôler l'affichage avec les fonctions 'and' et 'or'.

[[ company.partner_id.address and company.partner_id.address[0].street ]]

Dans le fichier corporate_rml_header, affiche la rue seulement si le partenaire est relié à un contact. La même instruction, sans la fonction 'and' génère une erreur car demande l'affichage d'une valeur qui n'existe pas (la rue d'un contact lorsqu'il n'y a pas de contact relié au partenaire).

Etat :

[[ o.partner_shipping_id.state_id and o.partner_shipping_id.state_id.name 
or removeParentNode('para') ]]

Pays:

[[ o.partner_shipping_id.country_id and o.partner_shipping_id.country_id.name 
or removeParentNode('para') ]]

Affiche le pays et l'état si il existe, si il n'existe pas, supprime le paragraphe avec le nom du champ.

Traduction

Le système de traduction des rapports fonctionne avec le même système que les traductions pour les textes dans les écrans.

Aller dans le menu 'Administration > Traduction > All terms'

Recherche dans le nom du rapport tous les textes de type 'rml'

Si vous ajoutez un nouveau texte dans un rapport, alors il faut aussi ajouter la traduction correspondante.

Ajouter une fonction

  • setlang('fr') Changer le code langue

[[ setLang(o.partner_id.lang) ]] Défini la langue comme celle du partenaire

  • repeatIn(list,varname) Repéter une ligne

[[ repeatIn(objects,'o') ]] Une boucle sur chaque objet

[[ repeatIn(o.invoice_line,'l') ]] Une boucle sur chaque ligne de l'objet

  • removeParentNodeN'affiche pas le parent (font, table, para, )
  • timeAffiche la date

[[ time.strftime('%d/%m/%Y') ]] Affiche la date au format dd MM YYYY

[[ time.ctime() ]] Affiche date et heure

[[ time.ctime().split()[3] ]] Affiche l'heure

  • userAffiche le user

[[ user.name ]] Affiche le nom du user

Créer un nouveau rapport

Declare a report in a XML file that must be added in the update_xml section of the __terp__.py file.

Report declaration

Here's an example of a XML file that declares a RML report :

<?xml version="1.0"?>
<terp>
<data>
<report id="sale_category_print"
string="Sales Orders By Categories"
model="sale.order"
name="sale_category.print"
rml="sale_category/report/sale_category_report.rml"
menu="True"
auto="False"/>
</data>
</terp>

A report is declared using a report tag inside a "data" block. The different arguments of a report tag are :

  • id : an identifier which must be unique.
  • string : the text of the menu that calls the report (if any, see below).
  • model : the Tiny ERP object on which the report will be rendered.
  • rml : the .RML report model. Important Note : Path is relative to addons/ directory.
  • menu : whether the report will be able to be called directly via the client or not. Setting menu to False is useful in case of reports called by wizards.
  • auto : determines if the .RML file must be parsed using the default parser or not. Using a custom parser allows you to define additional functions to your report.