This page describes how to build an ODT or ODS document (Format for OpenOffice, LibreOffice, …) template to make documents, using ODT/ODS generation.
Building a PDF template requires PHP development knowledge, but not building an ODT or ODS template.
ODT is the format of documents generated by LibreOffice Writer or Open Office (the most used case for document generation).
You can use both to generate a template Dolibarr can use.
Summary
1 Prerequisite
2 Create your document
3 Tags
3.1 Company/foundation information
3.2 Customers, prospects or suppliers information
3.3 Contacts
3.4 User information
3.5 Other information
3.6 Object information (invoice, commercial proposal, order, …)
3.7 Lines of object
3.8 For project documents
3.9 Conditional substitution
3.10 Other personalized tags
3.11 Other personalized tags for lines
4 Store your document
Prerequisite
Dolibarr: 3.1+
LibreOffice or any ODT or ODS editor (OpenOffice, …)
Create your document
Create an empty OpenOffice document from scratch and take the example provided. You may find them installed into a subdirectory of directory documents/doctemplates
Edit your document using all your Office suite features.
Include the tags you want to see in your document. Tags will be automatically replaced during the automatic generation of documents by Dolibarr. A list of all available tags is provided in the next chapter.
∇
Warning, tags must be rounded with {} or [] for arrays (see later) and must be typed with no stop using Office suite (with no backspace and no copy-paste). If not doing that, the Office suite adds some invisible information makings detection/replacement impossible. With LibreOffic,e you can also use the Ctrl+M to remove direct formatting from Tags
Tags
This is a list of tags that will be replaced with your information:
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
Tags for a contact of:
- invoice for invoices (type BILLING),
- contact of the proposal for proposals (type CUSTOMER),
- contact of order for sale orders (type CUSTOMER).
Also, other types of documents are not supported.
{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}{shipto_contact_name}
Delivery/Shipping contact
{shipto_contact_address}
{shipto_contact_zip}
{shipto_contact_town}
{shipto_contact_state}
{shipto_contact_country}
{shipto_contact_phone}
{shipto_contact_phone_person}
{shipto_contact_phone_mobile}
Extra fields:
{contact_options_xxx} : Value of extra field (where xxx is code of extra field)
For the documents generated from a third-party page (and only from this page), to output the list of all the default contacts of the third party, you may need also to surround the substitution variables with
[!– BEGIN companycontacts–][!– END companycontacts–]
so the system will know the beginning and end of each occurrence of contact to output.
User information
{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}
Other information
{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__}, …
Object information (invoice, commercial proposal, order, …)
Note: When you find _locale at the end of the name of a tag, it means the output value will be in the locale and language of the targetted language for generation instead of the default format or language.
{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 the 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 paid (numeric)
{object_already_deposit}
{object_already_creditnote}
{object_already_payed_all}
{object_remain_to_pay} : Remaining to pay the amount (numeric)
{object_already_payed_locale} : Amount already paid (output language format)
{object_already_deposit_locale}
{object_already_creditnote_locale}
{object_already_payed_all_locale}
{object_remain_to_pay_locale} : Remaining to pay the 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}
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)
Specific to the Purchase Order (Internal Purchasing Person):
{request_author}
{request_author_email}
For ODT with supplier prices, you may need also to surround the substitution variables with
[!– BEGIN supplierprices–][!– END supplierprices–]
INFORMATION
For the tag {object_total_…_x}, if there is no amount for the vat rate x, the key is not replaced. So you can use the condition syntax to show 0 instead of the key when value is not defined:
[!– IF {object_total_…_x} –]
0
[!– ELSE {object_total_…_x} –]
{object_total_…_x}
[!– ENDIF {object_total_…_x} –]
∇ To be sure this part of the code does not contain hidden text (added silently by LibreOffice for versioning/tracking change purposes), you must select all the code sections, make a right click and select the feature “Remove direct formatting” (not doing this may make the code section not working).
Lines of object
This is how to use arrays for lines of objects (invoices, commercial proposal, orders, etc…). You must create your array in the document and use a begin and end tag to define a line of the array. The line will then be repeated as much as required during generation.
[!– BEGIN row.lines –]
…
[!– END row.lines –]
If you want to display it not in tables row but in block lines :
[!– BEGIN lines –]
…
[!– END lines –]
Then, you can use the following tags in your line (in addition to any other tags of the document):
{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 field (where xxx is code of Product extra field)
This is an example of what you may have in your office suite:
https://wiki.dolibarr.org/index.php?title=Create_an_ODT_or_ODS_document_template#/media/File:Line_for_document_generation.png
https://wiki.dolibarr.org/index.php?title=Create_an_ODT_or_ODS_document_template#/media/File:Line_for_document_generation.png
For project documents
For documents about projects and tasks, you also have
{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 substitution 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 substitution variables with
[!– BEGIN tasksressources–][!– END tasksressources–]
[!– BEGIN taskstimes–][!– END taskstimes–]
[!– BEGIN tasksfiles–][!– END tasksfiles–]
[!– BEGIN projectfiles–][!– END projectfiles–]
[!– BEGIN projectcontacts–][!– END projectcontacts–]
Conditional substitution
Starting from Dolibarr v3.3, you can use conditional substitutions, which in layman’s terms means that you can decide to print something if a variable is true, or print something else if it’s false (or nothing at all) – including text but also any more complex structures like tables and images.
Example:
[!– 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: of course, the ELSE tag and block are optional, you can just use IF/ENDIF if you prefer.
∇ Warning: the format of this special tag is very precise and picky, be careful to put one space between:
[!–
IF/ELSE/ENDIF
{my var}
–]
∇ Warning: String must be typed with no stop using Office suite (with no backward and no copy-paste). If not doing that, the Office suite adds some invisible information making detection/replacement of text not possible.
Other personalized tags
If you want to add a field that is not predefined, this is the solution:
Create a file into the location:
htdocs/mymodule/core/substitutions/functions_mymodule.lib.php
Inside this file, just write one function like this:
/** 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;
}
The function will be called before generating a document to complete the array used for substitution and to replace tag myowntag with value $myvalue. You can add as many tags as you need and put all the personalized code you want to define values (search into the database, into files, calculation, from received parameters or global variables)…
∇ Warning: The first parameter in the function parameter declaration must start with & as the value is modified by your code and must be returned modified.
- To have your substitution function be called, if you have to :
- Check your substitution file is inside subdirectory module called htdocs/mymodule/core/substitutions.
- Check your module descriptor file contains an entry to declare this module has a substitution file
$this->module_parts = array(‘substitutions’ => 1)
- Enable your module (the module must be enabled and disabled each time you change the descriptor).
Note 2: this only allows to define global, static ODT variables (ie: to make dynamic variables that change for every line of product, please see the next chapter).
Other personalized tags for lines
This function has been available since Dolibarr v3.3 (future development version).
Similarly to the previous chapter, you have to define a function, and it works about the same, but with a few twists:
- Create or edit the same file as before (see the previous chapter).
- Here are the twists: in the file, write another function with almost the same name but with ‘_lines’ appended, and also add a new $lines variable:
/** 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;
}
Contrary to the previous function which was called only once, this function will be called every time a line of products will be processed, giving you each time a new $line object that you can process. This allows you to make different substitutions for each product.
Store your document
To see your document template appear in the list of available templates by going to the module configuration. In the ODT/ODS Template section, there is a small form to upload the template. It is also possible to place the file manually in the appropriate subdirectory located in the documents/doctemplates directory