Créer un modèle de document ODT ou ODS

Cette page décrit comment construire un modèle de document ODT ou ODS (Format de LibreOffice, OpenOffice, ...) pour générer un document automatiquement rempli par Dolibarr.

Pour savoir comment générer un modèle de document PDF, voir la page Créer un modèle de document PDF. Créer un modèle PDF requiert des connaissances en programmation PHP mais pas la création de modèles ODT.

  • ODT est le format de documents générés par LibreOffice Writer par exemple (cas le plus fréquemment utilisé pour la génération de document).
  • ODS est le format de documents générés par LibreOffice Calc par exemple.

Vous pouvez utiliser l'un ou l'autre comme modèle de document dans Dolibarr...

Prérequis

  • Dolibarr: 3.1+
  • LibreOffice ou tout éditeur de fichier ODT ou ODS (OpenOffice, ...)

Créer votre document

  • Créer un document OpenOffice ou LibreOffice en partant de rien ou en prenant un example fournis. Si vous partez d'un exemple, vous les trouverez installés dans les sous-répertoires du répertoire documents/doctemplates
  • Éditer le document en utilisant toutes les fonctions de votre suite bureautique.

Inclure dans votre documents les tags des informations que vous voulez voir. Les tags seront remplacés automatiquement au moment de la génération du document par Dolibarr. La liste des tags disponibles est décrite dans le chapitre suivant.

  Attention, les tags sont entourés de {} ou [] pour les tableaux ou conditions (voir plus loin) et doivent être tapés d'une traite sous la suite Office (sans retour arrière ou effacement, ni par copié-collé). Dans le cas contraire, la suite Office ajoutera des informations invisibles qui empêche le remplacement. Il est également possible d'utiliser les copier-coller mais en faisant un collage spécial sans formatage (CTRL+MAJ+V).

Tags des variables de substitution

Voici la liste des tags qui seront remplacés par les informations appropriées:

Company/foundation information

{mycompany_logo} : Your company logo
{mycompany_name} : Your company name
{mycompany_address} : Your company address
{mycompany_zip} : Your company zip
{mycompany_town} : Your company town
{mycompany_country} : Your company country (label)
{mycompany_country_code} : Your company country (code: FR, US, IT...)
{mycompany_state} : Your company state (label)
{mycompany_state_code} : Your company state (code)
{mycompany_phone}
{mycompany_fax}
{mycompany_email}
{mycompany_web}
{mycompany_barcode}
{mycompany_capital}
{mycompany_juridicalstatus}
{mycompany_managers}
{mycompany_idprof1}
{mycompany_idprof2}
{mycompany_idprof3}
{mycompany_idprof4}
{mycompany_idprof5}
{mycompany_idprof6}
{mycompany_vatnumber} : Your company VAT number
{mycompany_object}
{mycompany_note_private} : Your company private note

Customers, prospects or suppliers information

{company_name} : Customer or Supplier company name
{company_name_alias}
{company_address}
{company_zip}
{company_town}
{company_country} : Customer or Supplier company country (label)
{company_country_code} : Customer or Supplier company country (code: FR, US, IT...)
{company_state}
{company_state_code}
{company_phone}
{company_fax}
{company_email}
{company_web}
{company_barcode}
{company_customercode} : Company customer code
{company_suppliercode} : Company supplier code
{company_customeraccountancycode} : Company customer accountancy code
{company_supplieraccountancycode} : Company supplier accountancy code
{company_capital}
{company_juridicalstatus}
{company_outstanding_limit}
{company_idprof1}
{company_idprof2}
{company_idprof3}
{company_idprof4}
{company_idprof5}
{company_idprof6}
{company_vatnumber} : Customer or Supplier company VAT number
{company_note_public} : Customer or Supplier company public note
{company_note_private} : Customer or Supplier company private note
{company_default_bank_iban}
{company_default_bank_bic}

Extra fields:
{company_options_xxx} : Value of extra field (where xxx is code of extra field)

Contacts

Ce sont les balises pour :

  • contact de facture pour les factures (type BILLING)
  • contact d'appel d'offres (type CUSTOMER)
  • contact de commande pour les commandes de vente (type CUSTOMER)

Il n'y a pas de tags pour le contact de livraison et les autres objets ne peuvent utiliser ces tags non plus.

{contact_civility}
{contact_fullname}
{contact_lastname}
{contact_firstname}
{contact_address}
{contact_zip}
{contact_town}
{contact_state_id}
{contact_state_code}
{contact_state}
{contact_country_id}
{contact_country_code}
{contact_country}
{contact_poste}
{contact_socid}
{contact_statut}
{contact_code}
{contact_email}
{contact_jabberid}
{contact_phone_pro}
{contact_phone_perso}
{contact_phone_mobile}
{contact_fax}
{contact_birthday}
{contact_default_lang}
{contact_note_public}
{contact_note_private}

Extra fields:
{contact_options_xxx} : Value of extra field (where xxx is code of extra field)

For the documents generated from a thirdparty page (and only from this page), to output the list of all the default contacts of the thirdparty, you may need also to surround the substitutions variables with

[!-- BEGIN companycontacts--][!-- END companycontacts--]

so the system will know where is the begin and end of each occurence of contact to ouput.

Information utilisateur

{myuser_lastname}
{myuser_firstname}
{myuser_fullname}
{myuser_phone}
{myuser_fax}
{myuser_mobile}
{myuser_address}
{myuser_login}
{myuser_email}
{myuser_zip}
{myuser_town}
{myuser_country}
{myuser_country_code}
{myuser_state}
{myuser_state_code}
{myuser_logo}
{myuser_job}
{myuser_web}

Autres informations

{current_date}
{current_datehour}
{current_server_date}
{current_server_datehour}
{current_date_locale}
{current_datehour_locale}
{current_server_date_locale}
{current_server_datehour_locale}

{__(XXX)__} = Translated value of a translation key XXX - Note: Translation must be inside a language file that was previously loaded with $langs->load("languagefile")
{__[XXX]__} = Value of the constant XXX (into setup)

You may also use
{__XXX__} = To use a constant defined for email templates, that may also be available into ODT templates. For exemple {__ONLINE_PAYMENT_URL__}, {__DAY__}, {__REF__}, ...

Informations sur l'objet (facture, proposition commerciale, commande, ...)

{object_id}
{object_ref}
{object_ref_ext}
{object_label}
{object_ref_customer}
{object_ref_supplier}
{object_note_private}
{object_note_public}
or
{object_note}

Dates
{object_hour}
{object_date}
{object_date_rfc}
{object_date_creation}
{object_date_modification}
{object_date_validation}
{object_date_limit} : Specific to invoices
{object_date_end} : Specific to proposals : End date of validity of proposal
{object_date_delivery_planed} : Specific to order
{object_date_close}

Amounts (numeric data):
{object_total_ht}
{object_total_vat}
{object_total_localtax1}
{object_total_localtax2}
{object_total_ttc}
{object_total_discount_ht}
{object_total_vat_x} (is vat total for rate x. x can be for example 20, 8.5, 5.99 ... See also note*}

Amounts (in output language format):
{object_total_ht_locale} 
{object_total_vat_locale} 
{object_total_localtax1_locale}
{object_total_localtax2_locale}
{object_total_ttc_locale}
{object_total_discount_ht_locale}
{object_total_vat_locale_x} (is vat total for rate x. x can be for example 20, 8.5, 5.99 ... See also note*}

Multicurrency:
{object_multicurrency_code}
{object_multicurrency_tx}
{object_multicurrency_total_ht}
{object_multicurrency_total_tva}
{object_multicurrency_total_ttc}
{object_multicurrency_total_ht_locale}
{object_multicurrency_total_tva_locale}
{object_multicurrency_total_ttc_locale}

Specific to proposals :
{object_availability_id}
{object_availability_code}
{object_availability}

Specific to invoices :
{object_payment_mode}
{object_payment_mode_code}
{object_payment_term}
{object_payment_term_code}
{object_incoterms}
{object_bank_iban}
{object_bank_bic}
{object_bank_label}
{object_bank_number}
{object_bank_proprio}
{object_source_invoice_ref}
{object_already_payed} : Amount already payed (numeric)
{object_already_deposit}
{object_already_creditnote}
{object_already_payed_all}
{object_remain_to_pay} : Remaining to pay amount (numeric)
{object_already_payed_locale} : Amount already payed (output language format)
{object_already_deposit_locale}
{object_already_creditnote_locale}
{object_already_payed_all_locale}
{object_remain_to_pay_locale} : Remaining to pay amount (output language format)

Specific to Shipments :
{object_date_delivery}
{object_hour_delivery} 
{object_tracking_number}
{object_tracking_url}
{object_shipping_method}
{object_weight}
{object_width}
{object_height}
{object_depth}
{object_size}
{order_ref} : Ref of origin order used to generate the shipment
{order_ref_customer} : Ref on customer side of origin order used to generate the shipment

Specific to the Project linked to the object (if applicable):
{object_project_ref}
{object_project_title}
{object_project_description}
{object_project_date_start}
{object_project_date_end}

Specific to the Product directly linked to the object (if applicable):
{object_product_ref}
{object_product_label}

Extra fields:
{object_options_xxx} : Value of extra field (where xxx is code of extra field)

For ODT with supplier prices, you may need also to surround the substitutions variables with

[!-- BEGIN supplierprices--][!-- END supplierprices--]

 

Pour la balise {object_total..._x}, s'il n'y a pas de montant pour le taux de tva x, la clé n'est pas remplacée. Vous pouvez donc utiliser la syntaxe de condition pour afficher 0 au lieu de la clé lorsque la valeur n'est pas définie :

[!-- IF {object_total..._locale_x} --]
0
[!-- ELSE {object_total..._locale_x} --]
{object_total_vat_locale_x}
[!-- ENDIF {object_total..._locale_x} --]

  Pour être sûr que cette partie du code ne contient pas de texte caché (ajouté silencieusement par LibreOffice à des fins de gestion de version/suivi des changements), vous devez sélectionner toute la section de code, faire un clic droit et sélectionner la fonctionnalité "Supprimer le formatage direct" (ne pas faire cela peut rendre la section de code non opérationnelle).

Lignes des objets

Voici comment utiliser les tableaux de lignes des objets (lignes de factures, commandes, etc...). Vous devez créer un tableau dans le document et utiliser une balise "begin" et "end" pour définir la ligne du tableau. Cette ligne sera répétée autant de fois que requis au moment de la génération.

[!-- BEGIN row.lines --]
...
[!-- END row.lines --]

Ensuite, ajouter les tags de votre choix dans les lignes parmi ceux-ci (en plus des tags de documents également disponibles):

{line_pos}
{line_desc}
{line_product_ref}
{line_product_ref_fourn}
{line_product_label}
{line_product_type}
{line_product_barcode}
{line_vatrate}
{line_localtax1_rate}
{line_localtax2_rate}
{line_up}
{line_up_locale}
{line_qty}
{line_discount_percent}
{line_price_ht}
{line_price_ht_locale}
{line_price_vat}
{line_price_vat_locale}
{line_price_ttc}
{line_price_ttc_locale}
{line_date_start}
{line_date_start_locale}
{line_date_start_rfc}
{line_date_end}
{line_date_end_locale}
{line_date_end_rfc}
{line_unit} : Needs PRODUCT_USE_UNITS to 1
{line_unit_short} : Needs PRODUCT_USE_UNITS to 1

{line_fulldesc} : contains both line_desc and line_product_ref and line_product_label

Multicurrency :
{line_multicurrency_code}
{line_multicurrency_subprice}
{line_multicurrency_total_ht}
{line_multicurrency_total_tva}
{line_multicurrency_total_ttc}
{line_multicurrency_subprice_locale}
{line_multicurrency_total_ht_locale}
{line_multicurrency_total_tva_locale}
{line_multicurrency_total_ttc_locale}

Specific to Shipments :
{line_qty_shipped}
{line_qty_asked}
{line_weight}
{line_length}
{line_surface}
{line_volume}

Extra fields:
{line_options_xxx} : Value of extra field (where xxx is code of extra field)
{line_product_options_xxx} : If the line is a product, Value of the Product extra filed (where xxx is code of Product extra field)

Voici un exemple de ce que vous pourriez avoir dans votre traitement de texte:  

Pour les documents de projet

Pour les documents sur les projets et les tâches, vous avez également

{tasktime_rowid}
{tasktime_task_date}
{tasktime_task_duration}
{tasktime_task_duration_sec}
{tasktime_note}
{tasktime_fk_user}
{tasktime_user_name}
{tasktime_user_first}
{tasktime_fullcivname}

For projects ODT, you may need also to surround the substitutions variables with

[!-- BEGIN projectcontacts --][!-- END projectcontacts --]
[!-- BEGIN projectfiles --][!-- END projectfiles --]
[!-- BEGIN projectrefs --][!-- END projectrefs --]
[!-- BEGIN tasks --][!-- END tasks --]

For Tasks ODT, you may need also to surround the substitutions variables with

[!-- BEGIN tasksressources--][!-- END tasksressources--]
[!-- BEGIN taskstimes--][!-- END taskstimes--]
[!-- BEGIN tasksfiles--][!-- END tasksfiles--]
[!-- BEGIN projectfiles--][!-- END projectfiles--]
[!-- BEGIN projectcontacts--][!-- END projectcontacts--]

Substitution conditionnelle

Vous pouvez utiliser des remplacements conditionnels, ce qui en termes simples signifie que vous pouvez décider d'imprimer quelque chose si une variable est vrai, ou imprimer autre chose si elle est fausse (ou rien du tout) - et cela marche non seulement avec du texte, mais aussi toute structure plus complexe comme les tableaux et les images.

Exemple:

[!-- IF {my_var} --]
Print this text if {my_var} is true (can be any value but null/0/empty string)
[!-- ELSE {my_var} --]
Or print this if it's false (null/0/empty string)
[!-- ENDIF {my_var} --]

Note: le tag ELSE est bien sûr optionnel, vous pouvez sans problème juste utiliser un IF/ENDIF si vous préférez.

ATTENTION: le format de cette balise spéciale est très précis et pointilleux, faire attention à mettre un seul espace entre:

  • [!--
  • IF/ELSE/ENDIF
  • {my var}
  • --]

Exemple d'utilisation :

[!-- IF {line_product_label} --] {line_product_label} [!-- ELSE {line_product_label} --] {line_fulldesc} [!-- ENDIF {line_product_label} --]

Autres tags personnalisés

Si vous voulez ajouter un champ de substitution non prédéfini, il y a une solution:

  • Ajouter un fichier nommé htdocs/monmodule/core/substitutions/functions_mymodule.lib.php
  • Dans ce fichier , écrivez juste une fonction du genre:
/** 		Function called to complete substitution array (before generating on ODT, or a personalized email)
 * 		functions xxx_completesubstitutionarray are called by make_substitutions() if file
 * 		is inside directory htdocs/core/substitutions
 * 
 *		@param	array		$substitutionarray	Array with substitution key=>val
 *		@param	Translate	$langs			Output langs
 *		@param	Object		$object			Object to use to get values
 * 		@return	void					The entry parameter $substitutionarray is modified
 */
function mymodule_completesubstitutionarray(&$substitutionarray,$langs,$object)
{
   global $conf,$db;

   $myvalue='Put here calculated value to insert';
   $substitutionarray['myowntag']=$myvalue;
}

La fonction sera appelée avant la génération du document afin de compléter le tableau des substitutions afin que le tag myowntag soit remplacé par la valeur définie dans $myvalue. Vous pouvez ajouter autant de tag que désiré, et mettre le code que vous voulez pour définir les valeurs (recherche en base, en fichier, calcul, partir des variables reçues ou globales...).

Attention: Le premier paramètre dans la déclaration de la fonction doit commencer par & car il est modifié par le code et doit être retourné modifié.

Pour que vote votre fonction de substitution soit bien appelée, il vous faut:

  • Vérifier que votre fichier de substitution est bien stocké dans le répertoire htdocs/monmodule/core/substitutions
  • Créer un module (Voir la page Développement module) avec son fichier descripteur de module.
  • Vérifier que le fichier descripteur de module contient une entrée pour déclarer qu'il y a un fichier de substitution à appeler. Cette doit être
$this->module_parts = array('substitutions' => 1)
  • Activer le module (un module doit être activé et désactivé pour prendre en compte les changement dans son fichier descripteur)

Autres tags personnalisées pour les lignes

Cette fonctionnalité est disponible depuis Dolibarr v3.3 (version de développement futur).

De manière similaire au chapitre précédent, vous devez définir une fonction de la même façon mais avec quelques différences:

  • Créez ou modifiez le même fichier que précédemment (voir le chapitre précédent).
  • Voici les différences: dans le fichier, écrire une autre fonction avec presque le même nom mais avec '_lines' en suffixe, et également ajouter une nouvelle variable $lines:
/** 		Function called to complete substitution array for lines (before generating on ODT, or a personalized email)
 * 		functions xxx_completesubstitutionarray_lines are called by make_substitutions() if file
 * 		is inside directory htdocs/core/substitutions
 * 
 *		@param	array		$substitutionarray	Array with substitution key=>val
 *		@param	Translate	$langs			Output langs
 *		@param	Object		$object			Object to use to get values
 *              @param  Object          $line                   Current line being processed, use this object to get values
 * 		@return	void					The entry parameter $substitutionarray is modified
 */
function mymodule_completesubstitutionarray_lines(&$substitutionarray,$langs,$object,$line) {
{
   global $conf,$db;

   $myvalue='Put here calculated value to insert';
   $substitutionarray['myowntag']=$myvalue;
}

Contrairement à la fonction précédente qui n'est jamais appelée qu'une seule fois, cette fonction sera appelée à chaque fois qu'une ligne de produit sera traitée, en vous donnant chaque fois un objet $line différent que vous pouvez traiter. Cela vous permet de faire des substitutions de tags différentes pour chaque produit.

Enregistrer votre document

Pour voir apparaître votre modèle de document dans la liste des modèles disponibles en allant dans la configuration du module. Dans la section Modèle ODT/ODS, il y a un petit formulaire pour téléverser le modèle. Il est aussi possible de placer le fichier manuellement dans le sous-répertoire adéquat qui se trouve dans le répertoire documents/doctemplates